hls.js 970 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403
  1. (function webpackUniversalModuleDefinition(root, factory) {
  2. if (typeof exports === 'object' && typeof module === 'object')
  3. module.exports = factory();
  4. else if (typeof define === 'function' && define.amd)
  5. define([], factory);
  6. else if (typeof exports === 'object')
  7. exports["Hls"] = factory();
  8. else
  9. root["Hls"] = factory();
  10. })(this, function () {
  11. return /******/ (function (modules) { // webpackBootstrap
  12. /******/ // The module cache
  13. /******/
  14. var installedModules = {};
  15. /******/
  16. /******/ // The require function
  17. /******/
  18. function __webpack_require__(moduleId) {
  19. /******/
  20. /******/ // Check if module is in cache
  21. /******/
  22. if (installedModules[moduleId]) {
  23. /******/
  24. return installedModules[moduleId].exports;
  25. /******/
  26. }
  27. /******/ // Create a new module (and put it into the cache)
  28. /******/
  29. var module = installedModules[moduleId] = {
  30. /******/ i: moduleId,
  31. /******/ l: false,
  32. /******/ exports: {}
  33. /******/
  34. };
  35. /******/
  36. /******/ // Execute the module function
  37. /******/
  38. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  39. /******/
  40. /******/ // Flag the module as loaded
  41. /******/
  42. module.l = true;
  43. /******/
  44. /******/ // Return the exports of the module
  45. /******/
  46. return module.exports;
  47. /******/
  48. }
  49. /******/
  50. /******/
  51. /******/ // expose the modules object (__webpack_modules__)
  52. /******/
  53. __webpack_require__.m = modules;
  54. /******/
  55. /******/ // expose the module cache
  56. /******/
  57. __webpack_require__.c = installedModules;
  58. /******/
  59. /******/ // define getter function for harmony exports
  60. /******/
  61. __webpack_require__.d = function (exports, name, getter) {
  62. /******/
  63. if (!__webpack_require__.o(exports, name)) {
  64. /******/
  65. Object.defineProperty(exports, name, {
  66. /******/ configurable: false,
  67. /******/ enumerable: true,
  68. /******/ get: getter
  69. /******/
  70. });
  71. /******/
  72. }
  73. /******/
  74. };
  75. /******/
  76. /******/ // getDefaultExport function for compatibility with non-harmony modules
  77. /******/
  78. __webpack_require__.n = function (module) {
  79. /******/
  80. var getter = module && module.__esModule ?
  81. /******/ function getDefault() {
  82. return module['default'];
  83. } :
  84. /******/ function getModuleExports() {
  85. return module;
  86. };
  87. /******/
  88. __webpack_require__.d(getter, 'a', getter);
  89. /******/
  90. return getter;
  91. /******/
  92. };
  93. /******/
  94. /******/ // Object.prototype.hasOwnProperty.call
  95. /******/
  96. __webpack_require__.o = function (object, property) {
  97. return Object.prototype.hasOwnProperty.call(object, property);
  98. };
  99. /******/
  100. /******/ // __webpack_public_path__
  101. /******/
  102. __webpack_require__.p = "/dist/";
  103. /******/
  104. /******/ // Load entry module and return exports
  105. /******/
  106. return __webpack_require__(__webpack_require__.s = 9);
  107. /******/
  108. })
  109. /************************************************************************/
  110. /******/ ([
  111. /* 0 */
  112. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  113. "use strict";
  114. /* harmony export (binding) */
  115. __webpack_require__.d(__webpack_exports__, "a", function () {
  116. return enableLogs;
  117. });
  118. /* harmony export (binding) */
  119. __webpack_require__.d(__webpack_exports__, "b", function () {
  120. return logger;
  121. });
  122. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  123. return typeof obj;
  124. } : function (obj) {
  125. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  126. };
  127. function noop() {
  128. }
  129. var fakeLogger = {
  130. trace: noop,
  131. debug: noop,
  132. log: noop,
  133. warn: noop,
  134. info: noop,
  135. error: noop
  136. };
  137. var exportedLogger = fakeLogger;
  138. /*globals self: false */
  139. //let lastCallTime;
  140. // function formatMsgWithTimeInfo(type, msg) {
  141. // const now = Date.now();
  142. // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
  143. // lastCallTime = now;
  144. // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
  145. // return msg;
  146. // }
  147. function formatMsg(type, msg) {
  148. msg = '[' + type + '] > ' + msg;
  149. return msg;
  150. }
  151. function consolePrintFn(type) {
  152. var func = self.console[type];
  153. if (func) {
  154. return function () {
  155. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  156. args[_key] = arguments[_key];
  157. }
  158. if (args[0]) {
  159. args[0] = formatMsg(type, args[0]);
  160. }
  161. func.apply(self.console, args);
  162. };
  163. }
  164. return noop;
  165. }
  166. function exportLoggerFunctions(debugConfig) {
  167. for (var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  168. functions[_key2 - 1] = arguments[_key2];
  169. }
  170. functions.forEach(function (type) {
  171. exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
  172. });
  173. }
  174. var enableLogs = function enableLogs(debugConfig) {
  175. if (debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') {
  176. exportLoggerFunctions(debugConfig,
  177. // Remove out from list here to hard-disable a log-level
  178. //'trace',
  179. 'debug', 'log', 'info', 'warn', 'error');
  180. // Some browsers don't allow to use bind on console object anyway
  181. // fallback to default if needed
  182. try {
  183. exportedLogger.log();
  184. } catch (e) {
  185. exportedLogger = fakeLogger;
  186. }
  187. } else {
  188. exportedLogger = fakeLogger;
  189. }
  190. };
  191. var logger = exportedLogger;
  192. /***/
  193. }),
  194. /* 1 */
  195. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  196. "use strict";
  197. /* harmony default export */
  198. __webpack_exports__["a"] = ({
  199. // fired before MediaSource is attaching to media element - data: { media }
  200. MEDIA_ATTACHING: 'hlsMediaAttaching',
  201. // fired when MediaSource has been succesfully attached to media element - data: { }
  202. MEDIA_ATTACHED: 'hlsMediaAttached',
  203. // fired before detaching MediaSource from media element - data: { }
  204. MEDIA_DETACHING: 'hlsMediaDetaching',
  205. // fired when MediaSource has been detached from media element - data: { }
  206. MEDIA_DETACHED: 'hlsMediaDetached',
  207. // fired when we buffer is going to be reset - data: { }
  208. BUFFER_RESET: 'hlsBufferReset',
  209. // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }}
  210. BUFFER_CODECS: 'hlsBufferCodecs',
  211. // fired when sourcebuffers have been created - data: { tracks : tracks }
  212. BUFFER_CREATED: 'hlsBufferCreated',
  213. // fired when we append a segment to the buffer - data: { segment: segment object }
  214. BUFFER_APPENDING: 'hlsBufferAppending',
  215. // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent}
  216. BUFFER_APPENDED: 'hlsBufferAppended',
  217. // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { }
  218. BUFFER_EOS: 'hlsBufferEos',
  219. // fired when the media buffer should be flushed - data { startOffset, endOffset }
  220. BUFFER_FLUSHING: 'hlsBufferFlushing',
  221. // fired when the media buffer has been flushed - data: { }
  222. BUFFER_FLUSHED: 'hlsBufferFlushed',
  223. // fired to signal that a manifest loading starts - data: { url : manifestURL}
  224. MANIFEST_LOADING: 'hlsManifestLoading',
  225. // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}}
  226. MANIFEST_LOADED: 'hlsManifestLoaded',
  227. // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest}
  228. MANIFEST_PARSED: 'hlsManifestParsed',
  229. // fired when a level switch is requested - data: { level : id of new level } // deprecated in favor LEVEL_SWITCHING
  230. LEVEL_SWITCH: 'hlsLevelSwitch',
  231. // fired when a level switch is requested - data: { level : id of new level }
  232. LEVEL_SWITCHING: 'hlsLevelSwitching',
  233. // fired when a level switch is effective - data: { level : id of new level }
  234. LEVEL_SWITCHED: 'hlsLevelSwitched',
  235. // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded}
  236. LEVEL_LOADING: 'hlsLevelLoading',
  237. // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} }
  238. LEVEL_LOADED: 'hlsLevelLoaded',
  239. // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level }
  240. LEVEL_UPDATED: 'hlsLevelUpdated',
  241. // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
  242. LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
  243. // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks }
  244. AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated',
  245. // fired when an audio track switch occurs - data: { id : audio track id } // deprecated in favor AUDIO_TRACK_SWITCHING
  246. AUDIO_TRACK_SWITCH: 'hlsAudioTrackSwitch',
  247. // fired when an audio track switching is requested - data: { id : audio track id }
  248. AUDIO_TRACK_SWITCHING: 'hlsAudioTrackSwitching',
  249. // fired when an audio track switch actually occurs - data: { id : audio track id }
  250. AUDIO_TRACK_SWITCHED: 'hlsAudioTrackSwitched',
  251. // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id }
  252. AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading',
  253. // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } }
  254. AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded',
  255. // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks }
  256. SUBTITLE_TRACKS_UPDATED: 'hlsSubtitleTracksUpdated',
  257. // fired when an subtitle track switch occurs - data: { id : subtitle track id }
  258. SUBTITLE_TRACK_SWITCH: 'hlsSubtitleTrackSwitch',
  259. // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id }
  260. SUBTITLE_TRACK_LOADING: 'hlsSubtitleTrackLoading',
  261. // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : { trequest, tfirst, tload, mtime } }
  262. SUBTITLE_TRACK_LOADED: 'hlsSubtitleTrackLoaded',
  263. // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag }
  264. SUBTITLE_FRAG_PROCESSED: 'hlsSubtitleFragProcessed',
  265. // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, frag : fragment object }
  266. INIT_PTS_FOUND: 'hlsInitPtsFound',
  267. // fired when a fragment loading starts - data: { frag : fragment object }
  268. FRAG_LOADING: 'hlsFragLoading',
  269. // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } }
  270. FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress',
  271. // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object }
  272. FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted',
  273. // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } }
  274. FRAG_LOADED: 'hlsFragLoaded',
  275. // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, payload : fragment payload, stats : { tstart, tdecrypt } }
  276. FRAG_DECRYPTED: 'hlsFragDecrypted',
  277. // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment }
  278. FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
  279. // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] }
  280. FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata',
  281. // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] }
  282. FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
  283. // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}
  284. FRAG_PARSING_DATA: 'hlsFragParsingData',
  285. // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object }
  286. FRAG_PARSED: 'hlsFragParsed',
  287. // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length, bwEstimate } }
  288. FRAG_BUFFERED: 'hlsFragBuffered',
  289. // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object }
  290. FRAG_CHANGED: 'hlsFragChanged',
  291. // Identifier for a FPS drop event - data: { curentDropped, currentDecoded, totalDroppedFrames }
  292. FPS_DROP: 'hlsFpsDrop',
  293. //triggered when FPS drop triggers auto level capping - data: { level, droppedlevel }
  294. FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping',
  295. // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data }
  296. ERROR: 'hlsError',
  297. // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { }
  298. DESTROYING: 'hlsDestroying',
  299. // fired when a decrypt key loading starts - data: { frag : fragment object }
  300. KEY_LOADING: 'hlsKeyLoading',
  301. // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length } }
  302. KEY_LOADED: 'hlsKeyLoaded',
  303. // fired upon stream controller state transitions - data: { previousState, nextState }
  304. STREAM_STATE_TRANSITION: 'hlsStreamStateTransition'
  305. });
  306. /***/
  307. }),
  308. /* 2 */
  309. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  310. "use strict";
  311. /* harmony export (binding) */
  312. __webpack_require__.d(__webpack_exports__, "b", function () {
  313. return ErrorTypes;
  314. });
  315. /* harmony export (binding) */
  316. __webpack_require__.d(__webpack_exports__, "a", function () {
  317. return ErrorDetails;
  318. });
  319. var ErrorTypes = {
  320. // Identifier for a network error (loading error / timeout ...)
  321. NETWORK_ERROR: 'networkError',
  322. // Identifier for a media Error (video/parsing/mediasource error)
  323. MEDIA_ERROR: 'mediaError',
  324. // EME (encrypted media extensions) errors
  325. KEY_SYSTEM_ERROR: 'keySystemError',
  326. // Identifier for a mux Error (demuxing/remuxing)
  327. MUX_ERROR: 'muxError',
  328. // Identifier for all other errors
  329. OTHER_ERROR: 'otherError'
  330. };
  331. var ErrorDetails = {
  332. KEY_SYSTEM_NO_KEYS: 'keySystemNoKeys',
  333. KEY_SYSTEM_NO_ACCESS: 'keySystemNoAccess',
  334. KEY_SYSTEM_NO_SESSION: 'keySystemNoSession',
  335. KEY_SYSTEM_LICENSE_REQUEST_FAILED: 'keySystemLicenseRequestFailed',
  336. // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  337. MANIFEST_LOAD_ERROR: 'manifestLoadError',
  338. // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  339. MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut',
  340. // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason}
  341. MANIFEST_PARSING_ERROR: 'manifestParsingError',
  342. // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason}
  343. MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError',
  344. // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  345. LEVEL_LOAD_ERROR: 'levelLoadError',
  346. // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  347. LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut',
  348. // Identifier for a level switch error - data: { level : faulty level Id, event : error description}
  349. LEVEL_SWITCH_ERROR: 'levelSwitchError',
  350. // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  351. AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError',
  352. // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  353. AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut',
  354. // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }}
  355. FRAG_LOAD_ERROR: 'fragLoadError',
  356. // Identifier for fragment loop loading error - data: { frag : fragment object}
  357. FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError',
  358. // Identifier for fragment load timeout error - data: { frag : fragment object}
  359. FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut',
  360. // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description }
  361. FRAG_DECRYPT_ERROR: 'fragDecryptError',
  362. // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description }
  363. // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release
  364. FRAG_PARSING_ERROR: 'fragParsingError',
  365. // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text }
  366. REMUX_ALLOC_ERROR: 'remuxAllocError',
  367. // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }}
  368. KEY_LOAD_ERROR: 'keyLoadError',
  369. // Identifier for decrypt key load timeout error - data: { frag : fragment object}
  370. KEY_LOAD_TIMEOUT: 'keyLoadTimeOut',
  371. // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { err : exception , mimeType : mimeType }
  372. BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError',
  373. // Identifier for a buffer append error - data: append error description
  374. BUFFER_APPEND_ERROR: 'bufferAppendError',
  375. // Identifier for a buffer appending error event - data: appending error description
  376. BUFFER_APPENDING_ERROR: 'bufferAppendingError',
  377. // Identifier for a buffer stalled error event
  378. BUFFER_STALLED_ERROR: 'bufferStalledError',
  379. // Identifier for a buffer full event
  380. BUFFER_FULL_ERROR: 'bufferFullError',
  381. // Identifier for a buffer seek over hole event
  382. BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole',
  383. // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area)
  384. BUFFER_NUDGE_ON_STALL: 'bufferNudgeOnStall',
  385. // Identifier for an internal exception happening inside hls.js while handling an event
  386. INTERNAL_EXCEPTION: 'internalException'
  387. };
  388. /***/
  389. }),
  390. /* 3 */
  391. /***/ (function (module, exports, __webpack_require__) {
  392. // see https://tools.ietf.org/html/rfc1808
  393. /* jshint ignore:start */
  394. (function (root) {
  395. /* jshint ignore:end */
  396. var URL_REGEX = /^((?:[^\/;?#]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
  397. var FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/;
  398. var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
  399. var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
  400. var URLToolkit = { // jshint ignore:line
  401. // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
  402. // E.g
  403. // With opts.alwaysNormalize = false (default, spec compliant)
  404. // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
  405. // With opts.alwaysNormalize = true (default, not spec compliant)
  406. // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
  407. buildAbsoluteURL: function (baseURL, relativeURL, opts) {
  408. opts = opts || {};
  409. // remove any remaining space and CRLF
  410. baseURL = baseURL.trim();
  411. relativeURL = relativeURL.trim();
  412. if (!relativeURL) {
  413. // 2a) If the embedded URL is entirely empty, it inherits the
  414. // entire base URL (i.e., is set equal to the base URL)
  415. // and we are done.
  416. if (!opts.alwaysNormalize) {
  417. return baseURL;
  418. }
  419. var basePartsForNormalise = this.parseURL(baseURL);
  420. if (!baseParts) {
  421. throw new Error('Error trying to parse base URL.');
  422. }
  423. basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
  424. return URLToolkit.buildURLFromParts(basePartsForNormalise);
  425. }
  426. var relativeParts = this.parseURL(relativeURL);
  427. if (!relativeParts) {
  428. throw new Error('Error trying to parse relative URL.');
  429. }
  430. if (relativeParts.scheme) {
  431. // 2b) If the embedded URL starts with a scheme name, it is
  432. // interpreted as an absolute URL and we are done.
  433. if (!opts.alwaysNormalize) {
  434. return relativeURL;
  435. }
  436. relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
  437. return URLToolkit.buildURLFromParts(relativeParts);
  438. }
  439. var baseParts = this.parseURL(baseURL);
  440. if (!baseParts) {
  441. throw new Error('Error trying to parse base URL.');
  442. }
  443. if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
  444. // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
  445. // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
  446. var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
  447. baseParts.netLoc = pathParts[1];
  448. baseParts.path = pathParts[2];
  449. }
  450. if (baseParts.netLoc && !baseParts.path) {
  451. baseParts.path = '/';
  452. }
  453. var builtParts = {
  454. // 2c) Otherwise, the embedded URL inherits the scheme of
  455. // the base URL.
  456. scheme: baseParts.scheme,
  457. netLoc: relativeParts.netLoc,
  458. path: null,
  459. params: relativeParts.params,
  460. query: relativeParts.query,
  461. fragment: relativeParts.fragment
  462. };
  463. if (!relativeParts.netLoc) {
  464. // 3) If the embedded URL's <net_loc> is non-empty, we skip to
  465. // Step 7. Otherwise, the embedded URL inherits the <net_loc>
  466. // (if any) of the base URL.
  467. builtParts.netLoc = baseParts.netLoc;
  468. // 4) If the embedded URL path is preceded by a slash "/", the
  469. // path is not relative and we skip to Step 7.
  470. if (relativeParts.path[0] !== '/') {
  471. if (!relativeParts.path) {
  472. // 5) If the embedded URL path is empty (and not preceded by a
  473. // slash), then the embedded URL inherits the base URL path
  474. builtParts.path = baseParts.path;
  475. // 5a) if the embedded URL's <params> is non-empty, we skip to
  476. // step 7; otherwise, it inherits the <params> of the base
  477. // URL (if any) and
  478. if (!relativeParts.params) {
  479. builtParts.params = baseParts.params;
  480. // 5b) if the embedded URL's <query> is non-empty, we skip to
  481. // step 7; otherwise, it inherits the <query> of the base
  482. // URL (if any) and we skip to step 7.
  483. if (!relativeParts.query) {
  484. builtParts.query = baseParts.query;
  485. }
  486. }
  487. } else {
  488. // 6) The last segment of the base URL's path (anything
  489. // following the rightmost slash "/", or the entire path if no
  490. // slash is present) is removed and the embedded URL's path is
  491. // appended in its place.
  492. var baseURLPath = baseParts.path;
  493. var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
  494. builtParts.path = URLToolkit.normalizePath(newPath);
  495. }
  496. }
  497. }
  498. if (builtParts.path === null) {
  499. builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
  500. }
  501. return URLToolkit.buildURLFromParts(builtParts);
  502. },
  503. parseURL: function (url) {
  504. var parts = URL_REGEX.exec(url);
  505. if (!parts) {
  506. return null;
  507. }
  508. return {
  509. scheme: parts[1] || '',
  510. netLoc: parts[2] || '',
  511. path: parts[3] || '',
  512. params: parts[4] || '',
  513. query: parts[5] || '',
  514. fragment: parts[6] || ''
  515. };
  516. },
  517. normalizePath: function (path) {
  518. // The following operations are
  519. // then applied, in order, to the new path:
  520. // 6a) All occurrences of "./", where "." is a complete path
  521. // segment, are removed.
  522. // 6b) If the path ends with "." as a complete path segment,
  523. // that "." is removed.
  524. path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');
  525. // 6c) All occurrences of "<segment>/../", where <segment> is a
  526. // complete path segment not equal to "..", are removed.
  527. // Removal of these path segments is performed iteratively,
  528. // removing the leftmost matching pattern on each iteration,
  529. // until no matching pattern remains.
  530. // 6d) If the path ends with "<segment>/..", where <segment> is a
  531. // complete path segment not equal to "..", that
  532. // "<segment>/.." is removed.
  533. while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {
  534. } // jshint ignore:line
  535. return path.split('').reverse().join('');
  536. },
  537. buildURLFromParts: function (parts) {
  538. return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
  539. }
  540. };
  541. /* jshint ignore:start */
  542. if (true)
  543. module.exports = URLToolkit;
  544. else if (typeof define === 'function' && define.amd)
  545. define([], function () {
  546. return URLToolkit;
  547. });
  548. else if (typeof exports === 'object')
  549. exports["URLToolkit"] = URLToolkit;
  550. else
  551. root["URLToolkit"] = URLToolkit;
  552. })(this);
  553. /* jshint ignore:end */
  554. /***/
  555. }),
  556. /* 4 */
  557. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  558. "use strict";
  559. /* harmony export (binding) */
  560. __webpack_require__.d(__webpack_exports__, "b", function () {
  561. return utf8ArrayToStr;
  562. });
  563. function _classCallCheck(instance, Constructor) {
  564. if (!(instance instanceof Constructor)) {
  565. throw new TypeError("Cannot call a class as a function");
  566. }
  567. }
  568. /**
  569. * ID3 parser
  570. */
  571. var ID3 = function () {
  572. function ID3() {
  573. _classCallCheck(this, ID3);
  574. }
  575. /**
  576. * Returns true if an ID3 header can be found at offset in data
  577. * @param {Uint8Array} data - The data to search in
  578. * @param {number} offset - The offset at which to start searching
  579. * @return {boolean} - True if an ID3 header is found
  580. */
  581. ID3.isHeader = function isHeader(data, offset) {
  582. /*
  583. * http://id3.org/id3v2.3.0
  584. * [0] = 'I'
  585. * [1] = 'D'
  586. * [2] = '3'
  587. * [3,4] = {Version}
  588. * [5] = {Flags}
  589. * [6-9] = {ID3 Size}
  590. *
  591. * An ID3v2 tag can be detected with the following pattern:
  592. * $49 44 33 yy yy xx zz zz zz zz
  593. * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
  594. */
  595. if (offset + 10 <= data.length) {
  596. //look for 'ID3' identifier
  597. if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
  598. //check version is within range
  599. if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
  600. //check size is within range
  601. if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
  602. return true;
  603. }
  604. }
  605. }
  606. }
  607. return false;
  608. };
  609. /**
  610. * Returns true if an ID3 footer can be found at offset in data
  611. * @param {Uint8Array} data - The data to search in
  612. * @param {number} offset - The offset at which to start searching
  613. * @return {boolean} - True if an ID3 footer is found
  614. */
  615. ID3.isFooter = function isFooter(data, offset) {
  616. /*
  617. * The footer is a copy of the header, but with a different identifier
  618. */
  619. if (offset + 10 <= data.length) {
  620. //look for '3DI' identifier
  621. if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
  622. //check version is within range
  623. if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
  624. //check size is within range
  625. if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
  626. return true;
  627. }
  628. }
  629. }
  630. }
  631. return false;
  632. };
  633. /**
  634. * Returns any adjacent ID3 tags found in data starting at offset, as one block of data
  635. * @param {Uint8Array} data - The data to search in
  636. * @param {number} offset - The offset at which to start searching
  637. * @return {Uint8Array} - The block of data containing any ID3 tags found
  638. */
  639. ID3.getID3Data = function getID3Data(data, offset) {
  640. var front = offset;
  641. var length = 0;
  642. while (ID3.isHeader(data, offset)) {
  643. //ID3 header is 10 bytes
  644. length += 10;
  645. var size = ID3._readSize(data, offset + 6);
  646. length += size;
  647. if (ID3.isFooter(data, offset + 10)) {
  648. //ID3 footer is 10 bytes
  649. length += 10;
  650. }
  651. offset += length;
  652. }
  653. if (length > 0) {
  654. return data.subarray(front, front + length);
  655. }
  656. return undefined;
  657. };
  658. ID3._readSize = function _readSize(data, offset) {
  659. var size = 0;
  660. size = (data[offset] & 0x7f) << 21;
  661. size |= (data[offset + 1] & 0x7f) << 14;
  662. size |= (data[offset + 2] & 0x7f) << 7;
  663. size |= data[offset + 3] & 0x7f;
  664. return size;
  665. };
  666. /**
  667. * Searches for the Elementary Stream timestamp found in the ID3 data chunk
  668. * @param {Uint8Array} data - Block of data containing one or more ID3 tags
  669. * @return {number} - The timestamp
  670. */
  671. ID3.getTimeStamp = function getTimeStamp(data) {
  672. var frames = ID3.getID3Frames(data);
  673. for (var i = 0; i < frames.length; i++) {
  674. var frame = frames[i];
  675. if (ID3.isTimeStampFrame(frame)) {
  676. return ID3._readTimeStamp(frame);
  677. }
  678. }
  679. return undefined;
  680. };
  681. /**
  682. * Returns true if the ID3 frame is an Elementary Stream timestamp frame
  683. * @param {ID3 frame} frame
  684. */
  685. ID3.isTimeStampFrame = function isTimeStampFrame(frame) {
  686. return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
  687. };
  688. ID3._getFrameData = function _getFrameData(data) {
  689. /*
  690. Frame ID $xx xx xx xx (four characters)
  691. Size $xx xx xx xx
  692. Flags $xx xx
  693. */
  694. var type = String.fromCharCode(data[0], data[1], data[2], data[3]);
  695. var size = ID3._readSize(data, 4);
  696. //skip frame id, size, and flags
  697. var offset = 10;
  698. return {type: type, size: size, data: data.subarray(offset, offset + size)};
  699. };
  700. /**
  701. * Returns an array of ID3 frames found in all the ID3 tags in the id3Data
  702. * @param {Uint8Array} id3Data - The ID3 data containing one or more ID3 tags
  703. * @return {ID3 frame[]} - Array of ID3 frame objects
  704. */
  705. ID3.getID3Frames = function getID3Frames(id3Data) {
  706. var offset = 0;
  707. var frames = [];
  708. while (ID3.isHeader(id3Data, offset)) {
  709. var size = ID3._readSize(id3Data, offset + 6);
  710. //skip past ID3 header
  711. offset += 10;
  712. var end = offset + size;
  713. //loop through frames in the ID3 tag
  714. while (offset + 8 < end) {
  715. var frameData = ID3._getFrameData(id3Data.subarray(offset));
  716. var frame = ID3._decodeFrame(frameData);
  717. if (frame) {
  718. frames.push(frame);
  719. }
  720. //skip frame header and frame data
  721. offset += frameData.size + 10;
  722. }
  723. if (ID3.isFooter(id3Data, offset)) {
  724. offset += 10;
  725. }
  726. }
  727. return frames;
  728. };
  729. ID3._decodeFrame = function _decodeFrame(frame) {
  730. if (frame.type === 'PRIV') {
  731. return ID3._decodePrivFrame(frame);
  732. } else if (frame.type[0] === 'T') {
  733. return ID3._decodeTextFrame(frame);
  734. } else if (frame.type[0] === 'W') {
  735. return ID3._decodeURLFrame(frame);
  736. }
  737. return undefined;
  738. };
  739. ID3._readTimeStamp = function _readTimeStamp(timeStampFrame) {
  740. if (timeStampFrame.data.byteLength === 8) {
  741. var data = new Uint8Array(timeStampFrame.data);
  742. // timestamp is 33 bit expressed as a big-endian eight-octet number,
  743. // with the upper 31 bits set to zero.
  744. var pts33Bit = data[3] & 0x1;
  745. var timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
  746. timestamp /= 45;
  747. if (pts33Bit) {
  748. timestamp += 47721858.84; // 2^32 / 90
  749. }
  750. return Math.round(timestamp);
  751. }
  752. return undefined;
  753. };
  754. ID3._decodePrivFrame = function _decodePrivFrame(frame) {
  755. /*
  756. Format: <text string>\0<binary data>
  757. */
  758. if (frame.size < 2) {
  759. return undefined;
  760. }
  761. var owner = ID3._utf8ArrayToStr(frame.data, true);
  762. var privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
  763. return {key: frame.type, info: owner, data: privateData.buffer};
  764. };
  765. ID3._decodeTextFrame = function _decodeTextFrame(frame) {
  766. if (frame.size < 2) {
  767. return undefined;
  768. }
  769. if (frame.type === 'TXXX') {
  770. /*
  771. Format:
  772. [0] = {Text Encoding}
  773. [1-?] = {Description}\0{Value}
  774. */
  775. var index = 1;
  776. var description = ID3._utf8ArrayToStr(frame.data.subarray(index));
  777. index += description.length + 1;
  778. var value = ID3._utf8ArrayToStr(frame.data.subarray(index));
  779. return {key: frame.type, info: description, data: value};
  780. } else {
  781. /*
  782. Format:
  783. [0] = {Text Encoding}
  784. [1-?] = {Value}
  785. */
  786. var text = ID3._utf8ArrayToStr(frame.data.subarray(1));
  787. return {key: frame.type, data: text};
  788. }
  789. };
  790. ID3._decodeURLFrame = function _decodeURLFrame(frame) {
  791. if (frame.type === 'WXXX') {
  792. /*
  793. Format:
  794. [0] = {Text Encoding}
  795. [1-?] = {Description}\0{URL}
  796. */
  797. if (frame.size < 2) {
  798. return undefined;
  799. }
  800. var index = 1;
  801. var description = ID3._utf8ArrayToStr(frame.data.subarray(index));
  802. index += description.length + 1;
  803. var value = ID3._utf8ArrayToStr(frame.data.subarray(index));
  804. return {key: frame.type, info: description, data: value};
  805. } else {
  806. /*
  807. Format:
  808. [0-?] = {URL}
  809. */
  810. var url = ID3._utf8ArrayToStr(frame.data);
  811. return {key: frame.type, data: url};
  812. }
  813. };
  814. // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
  815. // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
  816. /* utf.js - UTF-8 <=> UTF-16 convertion
  817. *
  818. * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
  819. * Version: 1.0
  820. * LastModified: Dec 25 1999
  821. * This library is free. You can redistribute it and/or modify it.
  822. */
  823. ID3._utf8ArrayToStr = function _utf8ArrayToStr(array) {
  824. var exitOnNull = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  825. var len = array.length;
  826. var c = void 0;
  827. var char2 = void 0;
  828. var char3 = void 0;
  829. var out = '';
  830. var i = 0;
  831. while (i < len) {
  832. c = array[i++];
  833. if (c === 0x00 && exitOnNull) {
  834. return out;
  835. } else if (c === 0x00 || c === 0x03) {
  836. // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
  837. continue;
  838. }
  839. switch (c >> 4) {
  840. case 0:
  841. case 1:
  842. case 2:
  843. case 3:
  844. case 4:
  845. case 5:
  846. case 6:
  847. case 7:
  848. // 0xxxxxxx
  849. out += String.fromCharCode(c);
  850. break;
  851. case 12:
  852. case 13:
  853. // 110x xxxx 10xx xxxx
  854. char2 = array[i++];
  855. out += String.fromCharCode((c & 0x1F) << 6 | char2 & 0x3F);
  856. break;
  857. case 14:
  858. // 1110 xxxx 10xx xxxx 10xx xxxx
  859. char2 = array[i++];
  860. char3 = array[i++];
  861. out += String.fromCharCode((c & 0x0F) << 12 | (char2 & 0x3F) << 6 | (char3 & 0x3F) << 0);
  862. break;
  863. default:
  864. }
  865. }
  866. return out;
  867. };
  868. return ID3;
  869. }();
  870. var utf8ArrayToStr = ID3._utf8ArrayToStr;
  871. /* harmony default export */
  872. __webpack_exports__["a"] = (ID3);
  873. /***/
  874. }),
  875. /* 5 */
  876. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  877. "use strict";
  878. // CONCATENATED MODULE: ./src/crypt/aes-crypto.js
  879. function _classCallCheck(instance, Constructor) {
  880. if (!(instance instanceof Constructor)) {
  881. throw new TypeError("Cannot call a class as a function");
  882. }
  883. }
  884. var AESCrypto = function () {
  885. function AESCrypto(subtle, iv) {
  886. _classCallCheck(this, AESCrypto);
  887. this.subtle = subtle;
  888. this.aesIV = iv;
  889. }
  890. AESCrypto.prototype.decrypt = function decrypt(data, key) {
  891. return this.subtle.decrypt({name: 'AES-CBC', iv: this.aesIV}, key, data);
  892. };
  893. return AESCrypto;
  894. }();
  895. /* harmony default export */
  896. var aes_crypto = (AESCrypto);
  897. // CONCATENATED MODULE: ./src/crypt/fast-aes-key.js
  898. function fast_aes_key__classCallCheck(instance, Constructor) {
  899. if (!(instance instanceof Constructor)) {
  900. throw new TypeError("Cannot call a class as a function");
  901. }
  902. }
  903. var FastAESKey = function () {
  904. function FastAESKey(subtle, key) {
  905. fast_aes_key__classCallCheck(this, FastAESKey);
  906. this.subtle = subtle;
  907. this.key = key;
  908. }
  909. FastAESKey.prototype.expandKey = function expandKey() {
  910. return this.subtle.importKey('raw', this.key, {name: 'AES-CBC'}, false, ['encrypt', 'decrypt']);
  911. };
  912. return FastAESKey;
  913. }();
  914. /* harmony default export */
  915. var fast_aes_key = (FastAESKey);
  916. // CONCATENATED MODULE: ./src/crypt/aes-decryptor.js
  917. function aes_decryptor__classCallCheck(instance, Constructor) {
  918. if (!(instance instanceof Constructor)) {
  919. throw new TypeError("Cannot call a class as a function");
  920. }
  921. }
  922. var AESDecryptor = function () {
  923. function AESDecryptor() {
  924. aes_decryptor__classCallCheck(this, AESDecryptor);
  925. // Static after running initTable
  926. this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
  927. this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
  928. this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
  929. this.sBox = new Uint32Array(256);
  930. this.invSBox = new Uint32Array(256);
  931. // Changes during runtime
  932. this.key = new Uint32Array(0);
  933. this.initTable();
  934. }
  935. // Using view.getUint32() also swaps the byte order.
  936. AESDecryptor.prototype.uint8ArrayToUint32Array_ = function uint8ArrayToUint32Array_(arrayBuffer) {
  937. var view = new DataView(arrayBuffer);
  938. var newArray = new Uint32Array(4);
  939. for (var i = 0; i < 4; i++) {
  940. newArray[i] = view.getUint32(i * 4);
  941. }
  942. return newArray;
  943. };
  944. AESDecryptor.prototype.initTable = function initTable() {
  945. var sBox = this.sBox;
  946. var invSBox = this.invSBox;
  947. var subMix = this.subMix;
  948. var subMix0 = subMix[0];
  949. var subMix1 = subMix[1];
  950. var subMix2 = subMix[2];
  951. var subMix3 = subMix[3];
  952. var invSubMix = this.invSubMix;
  953. var invSubMix0 = invSubMix[0];
  954. var invSubMix1 = invSubMix[1];
  955. var invSubMix2 = invSubMix[2];
  956. var invSubMix3 = invSubMix[3];
  957. var d = new Uint32Array(256);
  958. var x = 0;
  959. var xi = 0;
  960. var i = 0;
  961. for (i = 0; i < 256; i++) {
  962. if (i < 128) {
  963. d[i] = i << 1;
  964. } else {
  965. d[i] = i << 1 ^ 0x11b;
  966. }
  967. }
  968. for (i = 0; i < 256; i++) {
  969. var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;
  970. sx = sx >>> 8 ^ sx & 0xff ^ 0x63;
  971. sBox[x] = sx;
  972. invSBox[sx] = x;
  973. // Compute multiplication
  974. var x2 = d[x];
  975. var x4 = d[x2];
  976. var x8 = d[x4];
  977. // Compute sub/invSub bytes, mix columns tables
  978. var t = d[sx] * 0x101 ^ sx * 0x1010100;
  979. subMix0[x] = t << 24 | t >>> 8;
  980. subMix1[x] = t << 16 | t >>> 16;
  981. subMix2[x] = t << 8 | t >>> 24;
  982. subMix3[x] = t;
  983. // Compute inv sub bytes, inv mix columns tables
  984. t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
  985. invSubMix0[sx] = t << 24 | t >>> 8;
  986. invSubMix1[sx] = t << 16 | t >>> 16;
  987. invSubMix2[sx] = t << 8 | t >>> 24;
  988. invSubMix3[sx] = t;
  989. // Compute next counter
  990. if (!x) {
  991. x = xi = 1;
  992. } else {
  993. x = x2 ^ d[d[d[x8 ^ x2]]];
  994. xi ^= d[d[xi]];
  995. }
  996. }
  997. };
  998. AESDecryptor.prototype.expandKey = function expandKey(keyBuffer) {
  999. // convert keyBuffer to Uint32Array
  1000. var key = this.uint8ArrayToUint32Array_(keyBuffer);
  1001. var sameKey = true;
  1002. var offset = 0;
  1003. while (offset < key.length && sameKey) {
  1004. sameKey = key[offset] === this.key[offset];
  1005. offset++;
  1006. }
  1007. if (sameKey) {
  1008. return;
  1009. }
  1010. this.key = key;
  1011. var keySize = this.keySize = key.length;
  1012. if (keySize !== 4 && keySize !== 6 && keySize !== 8) {
  1013. throw new Error('Invalid aes key size=' + keySize);
  1014. }
  1015. var ksRows = this.ksRows = (keySize + 6 + 1) * 4;
  1016. var ksRow = void 0;
  1017. var invKsRow = void 0;
  1018. var keySchedule = this.keySchedule = new Uint32Array(ksRows);
  1019. var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);
  1020. var sbox = this.sBox;
  1021. var rcon = this.rcon;
  1022. var invSubMix = this.invSubMix;
  1023. var invSubMix0 = invSubMix[0];
  1024. var invSubMix1 = invSubMix[1];
  1025. var invSubMix2 = invSubMix[2];
  1026. var invSubMix3 = invSubMix[3];
  1027. var prev = void 0;
  1028. var t = void 0;
  1029. for (ksRow = 0; ksRow < ksRows; ksRow++) {
  1030. if (ksRow < keySize) {
  1031. prev = keySchedule[ksRow] = key[ksRow];
  1032. continue;
  1033. }
  1034. t = prev;
  1035. if (ksRow % keySize === 0) {
  1036. // Rot word
  1037. t = t << 8 | t >>> 24;
  1038. // Sub word
  1039. t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];
  1040. // Mix Rcon
  1041. t ^= rcon[ksRow / keySize | 0] << 24;
  1042. } else if (keySize > 6 && ksRow % keySize === 4) {
  1043. // Sub word
  1044. t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];
  1045. }
  1046. keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;
  1047. }
  1048. for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {
  1049. ksRow = ksRows - invKsRow;
  1050. if (invKsRow & 3) {
  1051. t = keySchedule[ksRow];
  1052. } else {
  1053. t = keySchedule[ksRow - 4];
  1054. }
  1055. if (invKsRow < 4 || ksRow <= 4) {
  1056. invKeySchedule[invKsRow] = t;
  1057. } else {
  1058. invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]];
  1059. }
  1060. invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;
  1061. }
  1062. };
  1063. // Adding this as a method greatly improves performance.
  1064. AESDecryptor.prototype.networkToHostOrderSwap = function networkToHostOrderSwap(word) {
  1065. return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
  1066. };
  1067. AESDecryptor.prototype.decrypt = function decrypt(inputArrayBuffer, offset, aesIV) {
  1068. var nRounds = this.keySize + 6;
  1069. var invKeySchedule = this.invKeySchedule;
  1070. var invSBOX = this.invSBox;
  1071. var invSubMix = this.invSubMix;
  1072. var invSubMix0 = invSubMix[0];
  1073. var invSubMix1 = invSubMix[1];
  1074. var invSubMix2 = invSubMix[2];
  1075. var invSubMix3 = invSubMix[3];
  1076. var initVector = this.uint8ArrayToUint32Array_(aesIV);
  1077. var initVector0 = initVector[0];
  1078. var initVector1 = initVector[1];
  1079. var initVector2 = initVector[2];
  1080. var initVector3 = initVector[3];
  1081. var inputInt32 = new Int32Array(inputArrayBuffer);
  1082. var outputInt32 = new Int32Array(inputInt32.length);
  1083. var t0 = void 0,
  1084. t1 = void 0,
  1085. t2 = void 0,
  1086. t3 = void 0;
  1087. var s0 = void 0,
  1088. s1 = void 0,
  1089. s2 = void 0,
  1090. s3 = void 0;
  1091. var inputWords0 = void 0,
  1092. inputWords1 = void 0,
  1093. inputWords2 = void 0,
  1094. inputWords3 = void 0;
  1095. var ksRow, i;
  1096. var swapWord = this.networkToHostOrderSwap;
  1097. while (offset < inputInt32.length) {
  1098. inputWords0 = swapWord(inputInt32[offset]);
  1099. inputWords1 = swapWord(inputInt32[offset + 1]);
  1100. inputWords2 = swapWord(inputInt32[offset + 2]);
  1101. inputWords3 = swapWord(inputInt32[offset + 3]);
  1102. s0 = inputWords0 ^ invKeySchedule[0];
  1103. s1 = inputWords3 ^ invKeySchedule[1];
  1104. s2 = inputWords2 ^ invKeySchedule[2];
  1105. s3 = inputWords1 ^ invKeySchedule[3];
  1106. ksRow = 4;
  1107. // Iterate through the rounds of decryption
  1108. for (i = 1; i < nRounds; i++) {
  1109. t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow];
  1110. t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
  1111. t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
  1112. t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
  1113. // Update state
  1114. s0 = t0;
  1115. s1 = t1;
  1116. s2 = t2;
  1117. s3 = t3;
  1118. ksRow = ksRow + 4;
  1119. }
  1120. // Shift rows, sub bytes, add round key
  1121. t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow];
  1122. t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
  1123. t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
  1124. t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
  1125. ksRow = ksRow + 3;
  1126. // Write
  1127. outputInt32[offset] = swapWord(t0 ^ initVector0);
  1128. outputInt32[offset + 1] = swapWord(t3 ^ initVector1);
  1129. outputInt32[offset + 2] = swapWord(t2 ^ initVector2);
  1130. outputInt32[offset + 3] = swapWord(t1 ^ initVector3);
  1131. // reset initVector to last 4 unsigned int
  1132. initVector0 = inputWords0;
  1133. initVector1 = inputWords1;
  1134. initVector2 = inputWords2;
  1135. initVector3 = inputWords3;
  1136. offset = offset + 4;
  1137. }
  1138. return outputInt32.buffer;
  1139. };
  1140. AESDecryptor.prototype.destroy = function destroy() {
  1141. this.key = undefined;
  1142. this.keySize = undefined;
  1143. this.ksRows = undefined;
  1144. this.sBox = undefined;
  1145. this.invSBox = undefined;
  1146. this.subMix = undefined;
  1147. this.invSubMix = undefined;
  1148. this.keySchedule = undefined;
  1149. this.invKeySchedule = undefined;
  1150. this.rcon = undefined;
  1151. };
  1152. return AESDecryptor;
  1153. }();
  1154. /* harmony default export */
  1155. var aes_decryptor = (AESDecryptor);
  1156. // EXTERNAL MODULE: ./src/errors.js
  1157. var errors = __webpack_require__(2);
  1158. // EXTERNAL MODULE: ./src/utils/logger.js
  1159. var logger = __webpack_require__(0);
  1160. // CONCATENATED MODULE: ./src/crypt/decrypter.js
  1161. function decrypter__classCallCheck(instance, Constructor) {
  1162. if (!(instance instanceof Constructor)) {
  1163. throw new TypeError("Cannot call a class as a function");
  1164. }
  1165. }
  1166. /*globals self: false */
  1167. var decrypter_Decrypter = function () {
  1168. function Decrypter(observer, config) {
  1169. decrypter__classCallCheck(this, Decrypter);
  1170. this.observer = observer;
  1171. this.config = config;
  1172. this.logEnabled = true;
  1173. try {
  1174. var browserCrypto = crypto ? crypto : self.crypto;
  1175. this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle;
  1176. } catch (e) {
  1177. }
  1178. this.disableWebCrypto = !this.subtle;
  1179. }
  1180. Decrypter.prototype.isSync = function isSync() {
  1181. return this.disableWebCrypto && this.config.enableSoftwareAES;
  1182. };
  1183. Decrypter.prototype.decrypt = function decrypt(data, key, iv, callback) {
  1184. var _this = this;
  1185. if (this.disableWebCrypto && this.config.enableSoftwareAES) {
  1186. if (this.logEnabled) {
  1187. logger["b" /* logger */].log('JS AES decrypt');
  1188. this.logEnabled = false;
  1189. }
  1190. var decryptor = this.decryptor;
  1191. if (!decryptor) {
  1192. this.decryptor = decryptor = new aes_decryptor();
  1193. }
  1194. decryptor.expandKey(key);
  1195. callback(decryptor.decrypt(data, 0, iv));
  1196. } else {
  1197. if (this.logEnabled) {
  1198. logger["b" /* logger */].log('WebCrypto AES decrypt');
  1199. this.logEnabled = false;
  1200. }
  1201. var subtle = this.subtle;
  1202. if (this.key !== key) {
  1203. this.key = key;
  1204. this.fastAesKey = new fast_aes_key(subtle, key);
  1205. }
  1206. this.fastAesKey.expandKey().then(function (aesKey) {
  1207. // decrypt using web crypto
  1208. var crypto = new aes_crypto(subtle, iv);
  1209. crypto.decrypt(data, aesKey).catch(function (err) {
  1210. _this.onWebCryptoError(err, data, key, iv, callback);
  1211. }).then(function (result) {
  1212. callback(result);
  1213. });
  1214. }).catch(function (err) {
  1215. _this.onWebCryptoError(err, data, key, iv, callback);
  1216. });
  1217. }
  1218. };
  1219. Decrypter.prototype.onWebCryptoError = function onWebCryptoError(err, data, key, iv, callback) {
  1220. if (this.config.enableSoftwareAES) {
  1221. logger["b" /* logger */].log('WebCrypto Error, disable WebCrypto API');
  1222. this.disableWebCrypto = true;
  1223. this.logEnabled = true;
  1224. this.decrypt(data, key, iv, callback);
  1225. } else {
  1226. logger["b" /* logger */].error('decrypting error : ' + err.message);
  1227. this.observer.trigger(Event.ERROR, {
  1228. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  1229. details: errors["a" /* ErrorDetails */].FRAG_DECRYPT_ERROR,
  1230. fatal: true,
  1231. reason: err.message
  1232. });
  1233. }
  1234. };
  1235. Decrypter.prototype.destroy = function destroy() {
  1236. var decryptor = this.decryptor;
  1237. if (decryptor) {
  1238. decryptor.destroy();
  1239. this.decryptor = undefined;
  1240. }
  1241. };
  1242. return Decrypter;
  1243. }();
  1244. /* harmony default export */
  1245. var decrypter = __webpack_exports__["a"] = (decrypter_Decrypter);
  1246. /***/
  1247. }),
  1248. /* 6 */
  1249. /***/ (function (module, exports) {
  1250. // Copyright Joyent, Inc. and other Node contributors.
  1251. //
  1252. // Permission is hereby granted, free of charge, to any person obtaining a
  1253. // copy of this software and associated documentation files (the
  1254. // "Software"), to deal in the Software without restriction, including
  1255. // without limitation the rights to use, copy, modify, merge, publish,
  1256. // distribute, sublicense, and/or sell copies of the Software, and to permit
  1257. // persons to whom the Software is furnished to do so, subject to the
  1258. // following conditions:
  1259. //
  1260. // The above copyright notice and this permission notice shall be included
  1261. // in all copies or substantial portions of the Software.
  1262. //
  1263. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  1264. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  1265. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  1266. // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  1267. // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  1268. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  1269. // USE OR OTHER DEALINGS IN THE SOFTWARE.
  1270. function EventEmitter() {
  1271. this._events = this._events || {};
  1272. this._maxListeners = this._maxListeners || undefined;
  1273. }
  1274. module.exports = EventEmitter;
  1275. // Backwards-compat with node 0.10.x
  1276. EventEmitter.EventEmitter = EventEmitter;
  1277. EventEmitter.prototype._events = undefined;
  1278. EventEmitter.prototype._maxListeners = undefined;
  1279. // By default EventEmitters will print a warning if more than 10 listeners are
  1280. // added to it. This is a useful default which helps finding memory leaks.
  1281. EventEmitter.defaultMaxListeners = 10;
  1282. // Obviously not all Emitters should be limited to 10. This function allows
  1283. // that to be increased. Set to zero for unlimited.
  1284. EventEmitter.prototype.setMaxListeners = function (n) {
  1285. if (!isNumber(n) || n < 0 || isNaN(n))
  1286. throw TypeError('n must be a positive number');
  1287. this._maxListeners = n;
  1288. return this;
  1289. };
  1290. EventEmitter.prototype.emit = function (type) {
  1291. var er, handler, len, args, i, listeners;
  1292. if (!this._events)
  1293. this._events = {};
  1294. // If there is no 'error' event listener then throw.
  1295. if (type === 'error') {
  1296. if (!this._events.error ||
  1297. (isObject(this._events.error) && !this._events.error.length)) {
  1298. er = arguments[1];
  1299. if (er instanceof Error) {
  1300. throw er; // Unhandled 'error' event
  1301. } else {
  1302. // At least give some kind of context to the user
  1303. var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
  1304. err.context = er;
  1305. throw err;
  1306. }
  1307. }
  1308. }
  1309. handler = this._events[type];
  1310. if (isUndefined(handler))
  1311. return false;
  1312. if (isFunction(handler)) {
  1313. switch (arguments.length) {
  1314. // fast cases
  1315. case 1:
  1316. handler.call(this);
  1317. break;
  1318. case 2:
  1319. handler.call(this, arguments[1]);
  1320. break;
  1321. case 3:
  1322. handler.call(this, arguments[1], arguments[2]);
  1323. break;
  1324. // slower
  1325. default:
  1326. args = Array.prototype.slice.call(arguments, 1);
  1327. handler.apply(this, args);
  1328. }
  1329. } else if (isObject(handler)) {
  1330. args = Array.prototype.slice.call(arguments, 1);
  1331. listeners = handler.slice();
  1332. len = listeners.length;
  1333. for (i = 0; i < len; i++)
  1334. listeners[i].apply(this, args);
  1335. }
  1336. return true;
  1337. };
  1338. EventEmitter.prototype.addListener = function (type, listener) {
  1339. var m;
  1340. if (!isFunction(listener))
  1341. throw TypeError('listener must be a function');
  1342. if (!this._events)
  1343. this._events = {};
  1344. // To avoid recursion in the case that type === "newListener"! Before
  1345. // adding it to the listeners, first emit "newListener".
  1346. if (this._events.newListener)
  1347. this.emit('newListener', type,
  1348. isFunction(listener.listener) ?
  1349. listener.listener : listener);
  1350. if (!this._events[type])
  1351. // Optimize the case of one listener. Don't need the extra array object.
  1352. this._events[type] = listener;
  1353. else if (isObject(this._events[type]))
  1354. // If we've already got an array, just append.
  1355. this._events[type].push(listener);
  1356. else
  1357. // Adding the second element, need to change to array.
  1358. this._events[type] = [this._events[type], listener];
  1359. // Check for listener leak
  1360. if (isObject(this._events[type]) && !this._events[type].warned) {
  1361. if (!isUndefined(this._maxListeners)) {
  1362. m = this._maxListeners;
  1363. } else {
  1364. m = EventEmitter.defaultMaxListeners;
  1365. }
  1366. if (m && m > 0 && this._events[type].length > m) {
  1367. this._events[type].warned = true;
  1368. console.error('(node) warning: possible EventEmitter memory ' +
  1369. 'leak detected. %d listeners added. ' +
  1370. 'Use emitter.setMaxListeners() to increase limit.',
  1371. this._events[type].length);
  1372. if (typeof console.trace === 'function') {
  1373. // not supported in IE 10
  1374. console.trace();
  1375. }
  1376. }
  1377. }
  1378. return this;
  1379. };
  1380. EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  1381. EventEmitter.prototype.once = function (type, listener) {
  1382. if (!isFunction(listener))
  1383. throw TypeError('listener must be a function');
  1384. var fired = false;
  1385. function g() {
  1386. this.removeListener(type, g);
  1387. if (!fired) {
  1388. fired = true;
  1389. listener.apply(this, arguments);
  1390. }
  1391. }
  1392. g.listener = listener;
  1393. this.on(type, g);
  1394. return this;
  1395. };
  1396. // emits a 'removeListener' event iff the listener was removed
  1397. EventEmitter.prototype.removeListener = function (type, listener) {
  1398. var list, position, length, i;
  1399. if (!isFunction(listener))
  1400. throw TypeError('listener must be a function');
  1401. if (!this._events || !this._events[type])
  1402. return this;
  1403. list = this._events[type];
  1404. length = list.length;
  1405. position = -1;
  1406. if (list === listener ||
  1407. (isFunction(list.listener) && list.listener === listener)) {
  1408. delete this._events[type];
  1409. if (this._events.removeListener)
  1410. this.emit('removeListener', type, listener);
  1411. } else if (isObject(list)) {
  1412. for (i = length; i-- > 0;) {
  1413. if (list[i] === listener ||
  1414. (list[i].listener && list[i].listener === listener)) {
  1415. position = i;
  1416. break;
  1417. }
  1418. }
  1419. if (position < 0)
  1420. return this;
  1421. if (list.length === 1) {
  1422. list.length = 0;
  1423. delete this._events[type];
  1424. } else {
  1425. list.splice(position, 1);
  1426. }
  1427. if (this._events.removeListener)
  1428. this.emit('removeListener', type, listener);
  1429. }
  1430. return this;
  1431. };
  1432. EventEmitter.prototype.removeAllListeners = function (type) {
  1433. var key, listeners;
  1434. if (!this._events)
  1435. return this;
  1436. // not listening for removeListener, no need to emit
  1437. if (!this._events.removeListener) {
  1438. if (arguments.length === 0)
  1439. this._events = {};
  1440. else if (this._events[type])
  1441. delete this._events[type];
  1442. return this;
  1443. }
  1444. // emit removeListener for all listeners on all events
  1445. if (arguments.length === 0) {
  1446. for (key in this._events) {
  1447. if (key === 'removeListener') continue;
  1448. this.removeAllListeners(key);
  1449. }
  1450. this.removeAllListeners('removeListener');
  1451. this._events = {};
  1452. return this;
  1453. }
  1454. listeners = this._events[type];
  1455. if (isFunction(listeners)) {
  1456. this.removeListener(type, listeners);
  1457. } else if (listeners) {
  1458. // LIFO order
  1459. while (listeners.length)
  1460. this.removeListener(type, listeners[listeners.length - 1]);
  1461. }
  1462. delete this._events[type];
  1463. return this;
  1464. };
  1465. EventEmitter.prototype.listeners = function (type) {
  1466. var ret;
  1467. if (!this._events || !this._events[type])
  1468. ret = [];
  1469. else if (isFunction(this._events[type]))
  1470. ret = [this._events[type]];
  1471. else
  1472. ret = this._events[type].slice();
  1473. return ret;
  1474. };
  1475. EventEmitter.prototype.listenerCount = function (type) {
  1476. if (this._events) {
  1477. var evlistener = this._events[type];
  1478. if (isFunction(evlistener))
  1479. return 1;
  1480. else if (evlistener)
  1481. return evlistener.length;
  1482. }
  1483. return 0;
  1484. };
  1485. EventEmitter.listenerCount = function (emitter, type) {
  1486. return emitter.listenerCount(type);
  1487. };
  1488. function isFunction(arg) {
  1489. return typeof arg === 'function';
  1490. }
  1491. function isNumber(arg) {
  1492. return typeof arg === 'number';
  1493. }
  1494. function isObject(arg) {
  1495. return typeof arg === 'object' && arg !== null;
  1496. }
  1497. function isUndefined(arg) {
  1498. return arg === void 0;
  1499. }
  1500. /***/
  1501. }),
  1502. /* 7 */
  1503. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  1504. "use strict";
  1505. /* harmony import */
  1506. var __WEBPACK_IMPORTED_MODULE_0__utils_logger__ = __webpack_require__(0);
  1507. /* harmony import */
  1508. var __WEBPACK_IMPORTED_MODULE_1__events__ = __webpack_require__(1);
  1509. function _classCallCheck(instance, Constructor) {
  1510. if (!(instance instanceof Constructor)) {
  1511. throw new TypeError("Cannot call a class as a function");
  1512. }
  1513. }
  1514. /**
  1515. * MP4 demuxer
  1516. */
  1517. var UINT32_MAX = Math.pow(2, 32) - 1;
  1518. var MP4Demuxer = function () {
  1519. function MP4Demuxer(observer, remuxer) {
  1520. _classCallCheck(this, MP4Demuxer);
  1521. this.observer = observer;
  1522. this.remuxer = remuxer;
  1523. }
  1524. MP4Demuxer.prototype.resetTimeStamp = function resetTimeStamp(initPTS) {
  1525. this.initPTS = initPTS;
  1526. };
  1527. MP4Demuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
  1528. //jshint unused:false
  1529. if (initSegment && initSegment.byteLength) {
  1530. var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment);
  1531. // default audio codec if nothing specified
  1532. // TODO : extract that from initsegment
  1533. if (audioCodec == null) {
  1534. audioCodec = 'mp4a.40.5';
  1535. }
  1536. if (videoCodec == null) {
  1537. videoCodec = 'avc1.42e01e';
  1538. }
  1539. var tracks = {};
  1540. if (initData.audio && initData.video) {
  1541. tracks.audiovideo = {
  1542. container: 'video/mp4',
  1543. codec: audioCodec + ',' + videoCodec,
  1544. initSegment: duration ? initSegment : null
  1545. };
  1546. } else {
  1547. if (initData.audio) {
  1548. tracks.audio = {
  1549. container: 'audio/mp4',
  1550. codec: audioCodec,
  1551. initSegment: duration ? initSegment : null
  1552. };
  1553. }
  1554. if (initData.video) {
  1555. tracks.video = {
  1556. container: 'video/mp4',
  1557. codec: videoCodec,
  1558. initSegment: duration ? initSegment : null
  1559. };
  1560. }
  1561. }
  1562. this.observer.trigger(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_INIT_SEGMENT, {tracks: tracks});
  1563. } else {
  1564. if (audioCodec) {
  1565. this.audioCodec = audioCodec;
  1566. }
  1567. if (videoCodec) {
  1568. this.videoCodec = videoCodec;
  1569. }
  1570. }
  1571. };
  1572. MP4Demuxer.probe = function probe(data) {
  1573. // ensure we find a moof box in the first 16 kB
  1574. return MP4Demuxer.findBox({
  1575. data: data,
  1576. start: 0,
  1577. end: Math.min(data.length, 16384)
  1578. }, ['moof']).length > 0;
  1579. };
  1580. MP4Demuxer.bin2str = function bin2str(buffer) {
  1581. return String.fromCharCode.apply(null, buffer);
  1582. };
  1583. MP4Demuxer.readUint16 = function readUint16(buffer, offset) {
  1584. if (buffer.data) {
  1585. offset += buffer.start;
  1586. buffer = buffer.data;
  1587. }
  1588. var val = buffer[offset] << 8 | buffer[offset + 1];
  1589. return val < 0 ? 65536 + val : val;
  1590. };
  1591. MP4Demuxer.readUint32 = function readUint32(buffer, offset) {
  1592. if (buffer.data) {
  1593. offset += buffer.start;
  1594. buffer = buffer.data;
  1595. }
  1596. var val = buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
  1597. return val < 0 ? 4294967296 + val : val;
  1598. };
  1599. MP4Demuxer.writeUint32 = function writeUint32(buffer, offset, value) {
  1600. if (buffer.data) {
  1601. offset += buffer.start;
  1602. buffer = buffer.data;
  1603. }
  1604. buffer[offset] = value >> 24;
  1605. buffer[offset + 1] = value >> 16 & 0xff;
  1606. buffer[offset + 2] = value >> 8 & 0xff;
  1607. buffer[offset + 3] = value & 0xff;
  1608. };
  1609. // Find the data for a box specified by its path
  1610. MP4Demuxer.findBox = function findBox(data, path) {
  1611. var results = [],
  1612. i,
  1613. size,
  1614. type,
  1615. end,
  1616. subresults,
  1617. start,
  1618. endbox;
  1619. if (data.data) {
  1620. start = data.start;
  1621. end = data.end;
  1622. data = data.data;
  1623. } else {
  1624. start = 0;
  1625. end = data.byteLength;
  1626. }
  1627. if (!path.length) {
  1628. // short-circuit the search for empty paths
  1629. return null;
  1630. }
  1631. for (i = start; i < end;) {
  1632. size = MP4Demuxer.readUint32(data, i);
  1633. type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8));
  1634. endbox = size > 1 ? i + size : end;
  1635. if (type === path[0]) {
  1636. if (path.length === 1) {
  1637. // this is the end of the path and we've found the box we were
  1638. // looking for
  1639. results.push({data: data, start: i + 8, end: endbox});
  1640. } else {
  1641. // recursively search for the next box along the path
  1642. subresults = MP4Demuxer.findBox({
  1643. data: data,
  1644. start: i + 8,
  1645. end: endbox
  1646. }, path.slice(1));
  1647. if (subresults.length) {
  1648. results = results.concat(subresults);
  1649. }
  1650. }
  1651. }
  1652. i = endbox;
  1653. }
  1654. // we've finished searching all of data
  1655. return results;
  1656. };
  1657. MP4Demuxer.parseSegmentIndex = function parseSegmentIndex(initSegment) {
  1658. var moov = MP4Demuxer.findBox(initSegment, ['moov'])[0];
  1659. var moovEndOffset = moov ? moov.end : null; // we need this in case we need to chop of garbage of the end of current data
  1660. var index = 0;
  1661. var sidx = MP4Demuxer.findBox(initSegment, ['sidx']);
  1662. var references = void 0;
  1663. if (!sidx || !sidx[0]) {
  1664. return null;
  1665. }
  1666. references = [];
  1667. sidx = sidx[0];
  1668. var version = sidx.data[0];
  1669. // set initial offset, we skip the reference ID (not needed)
  1670. index = version === 0 ? 8 : 16;
  1671. var timescale = MP4Demuxer.readUint32(sidx, index);
  1672. index += 4;
  1673. // TODO: parse earliestPresentationTime and firstOffset
  1674. // usually zero in our case
  1675. var earliestPresentationTime = 0;
  1676. var firstOffset = 0;
  1677. if (version === 0) {
  1678. index += 8;
  1679. } else {
  1680. index += 16;
  1681. }
  1682. // skip reserved
  1683. index += 2;
  1684. var startByte = sidx.end + firstOffset;
  1685. var referencesCount = MP4Demuxer.readUint16(sidx, index);
  1686. index += 2;
  1687. for (var i = 0; i < referencesCount; i++) {
  1688. var referenceIndex = index;
  1689. var referenceInfo = MP4Demuxer.readUint32(sidx, referenceIndex);
  1690. referenceIndex += 4;
  1691. var referenceSize = referenceInfo & 0x7FFFFFFF;
  1692. var referenceType = (referenceInfo & 0x80000000) >>> 31;
  1693. if (referenceType === 1) {
  1694. console.warn('SIDX has hierarchical references (not supported)');
  1695. return;
  1696. }
  1697. var subsegmentDuration = MP4Demuxer.readUint32(sidx, referenceIndex);
  1698. referenceIndex += 4;
  1699. references.push({
  1700. referenceSize: referenceSize,
  1701. subsegmentDuration: subsegmentDuration, // unscaled
  1702. info: {
  1703. duration: subsegmentDuration / timescale,
  1704. start: startByte,
  1705. end: startByte + referenceSize - 1
  1706. }
  1707. });
  1708. startByte += referenceSize;
  1709. // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits
  1710. // for |sapDelta|.
  1711. referenceIndex += 4;
  1712. // skip to next ref
  1713. index = referenceIndex;
  1714. }
  1715. return {
  1716. earliestPresentationTime: earliestPresentationTime,
  1717. timescale: timescale,
  1718. version: version,
  1719. referencesCount: referencesCount,
  1720. references: references,
  1721. moovEndOffset: moovEndOffset
  1722. };
  1723. };
  1724. /**
  1725. * Parses an MP4 initialization segment and extracts stream type and
  1726. * timescale values for any declared tracks. Timescale values indicate the
  1727. * number of clock ticks per second to assume for time-based values
  1728. * elsewhere in the MP4.
  1729. *
  1730. * To determine the start time of an MP4, you need two pieces of
  1731. * information: the timescale unit and the earliest base media decode
  1732. * time. Multiple timescales can be specified within an MP4 but the
  1733. * base media decode time is always expressed in the timescale from
  1734. * the media header box for the track:
  1735. * ```
  1736. * moov > trak > mdia > mdhd.timescale
  1737. * moov > trak > mdia > hdlr
  1738. * ```
  1739. * @param init {Uint8Array} the bytes of the init segment
  1740. * @return {object} a hash of track type to timescale values or null if
  1741. * the init segment is malformed.
  1742. */
  1743. MP4Demuxer.parseInitSegment = function parseInitSegment(initSegment) {
  1744. var result = [];
  1745. var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']);
  1746. traks.forEach(function (trak) {
  1747. var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0];
  1748. if (tkhd) {
  1749. var version = tkhd.data[tkhd.start];
  1750. var index = version === 0 ? 12 : 20;
  1751. var trackId = MP4Demuxer.readUint32(tkhd, index);
  1752. var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0];
  1753. if (mdhd) {
  1754. version = mdhd.data[mdhd.start];
  1755. index = version === 0 ? 12 : 20;
  1756. var timescale = MP4Demuxer.readUint32(mdhd, index);
  1757. var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0];
  1758. if (hdlr) {
  1759. var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12));
  1760. var type = {'soun': 'audio', 'vide': 'video'}[hdlrType];
  1761. if (type) {
  1762. // extract codec info. TODO : parse codec details to be able to build MIME type
  1763. var codecBox = MP4Demuxer.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd']);
  1764. if (codecBox.length) {
  1765. codecBox = codecBox[0];
  1766. var codecType = MP4Demuxer.bin2str(codecBox.data.subarray(codecBox.start + 12, codecBox.start + 16));
  1767. __WEBPACK_IMPORTED_MODULE_0__utils_logger__["b" /* logger */].log('MP4Demuxer:' + type + ':' + codecType + ' found');
  1768. }
  1769. result[trackId] = {timescale: timescale, type: type};
  1770. result[type] = {timescale: timescale, id: trackId};
  1771. }
  1772. }
  1773. }
  1774. }
  1775. });
  1776. return result;
  1777. };
  1778. /**
  1779. * Determine the base media decode start time, in seconds, for an MP4
  1780. * fragment. If multiple fragments are specified, the earliest time is
  1781. * returned.
  1782. *
  1783. * The base media decode time can be parsed from track fragment
  1784. * metadata:
  1785. * ```
  1786. * moof > traf > tfdt.baseMediaDecodeTime
  1787. * ```
  1788. * It requires the timescale value from the mdhd to interpret.
  1789. *
  1790. * @param timescale {object} a hash of track ids to timescale values.
  1791. * @return {number} the earliest base media decode start time for the
  1792. * fragment, in seconds
  1793. */
  1794. MP4Demuxer.getStartDTS = function getStartDTS(initData, fragment) {
  1795. var trafs, baseTimes, result;
  1796. // we need info from two childrend of each track fragment box
  1797. trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']);
  1798. // determine the start times for each track
  1799. baseTimes = [].concat.apply([], trafs.map(function (traf) {
  1800. return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
  1801. var id, scale, baseTime;
  1802. // get the track id from the tfhd
  1803. id = MP4Demuxer.readUint32(tfhd, 4);
  1804. // assume a 90kHz clock if no timescale was specified
  1805. scale = initData[id].timescale || 90e3;
  1806. // get the base media decode time from the tfdt
  1807. baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
  1808. var version, result;
  1809. version = tfdt.data[tfdt.start];
  1810. result = MP4Demuxer.readUint32(tfdt, 4);
  1811. if (version === 1) {
  1812. result *= Math.pow(2, 32);
  1813. result += MP4Demuxer.readUint32(tfdt, 8);
  1814. }
  1815. return result;
  1816. })[0];
  1817. // convert base time to seconds
  1818. return baseTime / scale;
  1819. });
  1820. }));
  1821. // return the minimum
  1822. result = Math.min.apply(null, baseTimes);
  1823. return isFinite(result) ? result : 0;
  1824. };
  1825. MP4Demuxer.offsetStartDTS = function offsetStartDTS(initData, fragment, timeOffset) {
  1826. MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) {
  1827. return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
  1828. // get the track id from the tfhd
  1829. var id = MP4Demuxer.readUint32(tfhd, 4);
  1830. // assume a 90kHz clock if no timescale was specified
  1831. var timescale = initData[id].timescale || 90e3;
  1832. // get the base media decode time from the tfdt
  1833. MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
  1834. var version = tfdt.data[tfdt.start];
  1835. var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4);
  1836. if (version === 0) {
  1837. MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale);
  1838. } else {
  1839. baseMediaDecodeTime *= Math.pow(2, 32);
  1840. baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8);
  1841. baseMediaDecodeTime -= timeOffset * timescale;
  1842. baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0);
  1843. var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1));
  1844. var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
  1845. MP4Demuxer.writeUint32(tfdt, 4, upper);
  1846. MP4Demuxer.writeUint32(tfdt, 8, lower);
  1847. }
  1848. });
  1849. });
  1850. });
  1851. };
  1852. // feed incoming data to the front of the parsing pipeline
  1853. MP4Demuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
  1854. var initData = this.initData;
  1855. if (!initData) {
  1856. this.resetInitSegment(data, this.audioCodec, this.videoCodec, false);
  1857. initData = this.initData;
  1858. }
  1859. var startDTS = void 0,
  1860. initPTS = this.initPTS;
  1861. if (initPTS === undefined) {
  1862. var _startDTS = MP4Demuxer.getStartDTS(initData, data);
  1863. this.initPTS = initPTS = _startDTS - timeOffset;
  1864. this.observer.trigger(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].INIT_PTS_FOUND, {initPTS: initPTS});
  1865. }
  1866. MP4Demuxer.offsetStartDTS(initData, data, initPTS);
  1867. startDTS = MP4Demuxer.getStartDTS(initData, data);
  1868. this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data);
  1869. };
  1870. MP4Demuxer.prototype.destroy = function destroy() {
  1871. };
  1872. return MP4Demuxer;
  1873. }();
  1874. /* harmony default export */
  1875. __webpack_exports__["a"] = (MP4Demuxer);
  1876. /***/
  1877. }),
  1878. /* 8 */
  1879. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  1880. "use strict";
  1881. // EXTERNAL MODULE: ./src/events.js
  1882. var events = __webpack_require__(1);
  1883. // EXTERNAL MODULE: ./src/errors.js
  1884. var errors = __webpack_require__(2);
  1885. // EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules
  1886. var crypt_decrypter = __webpack_require__(5);
  1887. // EXTERNAL MODULE: ./src/utils/logger.js
  1888. var logger = __webpack_require__(0);
  1889. // CONCATENATED MODULE: ./src/demux/adts.js
  1890. /**
  1891. * ADTS parser helper
  1892. */
  1893. function getAudioConfig(observer, data, offset, audioCodec) {
  1894. var adtsObjectType,
  1895. // :int
  1896. adtsSampleingIndex,
  1897. // :int
  1898. adtsExtensionSampleingIndex,
  1899. // :int
  1900. adtsChanelConfig,
  1901. // :int
  1902. config,
  1903. userAgent = navigator.userAgent.toLowerCase(),
  1904. manifestCodec = audioCodec,
  1905. adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
  1906. // byte 2
  1907. adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1;
  1908. adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2;
  1909. if (adtsSampleingIndex > adtsSampleingRates.length - 1) {
  1910. observer.trigger(Event.ERROR, {
  1911. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  1912. details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR,
  1913. fatal: true,
  1914. reason: 'invalid ADTS sampling index:' + adtsSampleingIndex
  1915. });
  1916. return;
  1917. }
  1918. adtsChanelConfig = (data[offset + 2] & 0x01) << 2;
  1919. // byte 3
  1920. adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6;
  1921. logger["b" /* logger */].log('manifest codec:' + audioCodec + ',ADTS data:type:' + adtsObjectType + ',sampleingIndex:' + adtsSampleingIndex + '[' + adtsSampleingRates[adtsSampleingIndex] + 'Hz],channelConfig:' + adtsChanelConfig);
  1922. // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
  1923. if (/firefox/i.test(userAgent)) {
  1924. if (adtsSampleingIndex >= 6) {
  1925. adtsObjectType = 5;
  1926. config = new Array(4);
  1927. // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
  1928. // there is a factor 2 between frame sample rate and output sample rate
  1929. // multiply frequency by 2 (see table below, equivalent to substract 3)
  1930. adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
  1931. } else {
  1932. adtsObjectType = 2;
  1933. config = new Array(2);
  1934. adtsExtensionSampleingIndex = adtsSampleingIndex;
  1935. }
  1936. // Android : always use AAC
  1937. } else if (userAgent.indexOf('android') !== -1) {
  1938. adtsObjectType = 2;
  1939. config = new Array(2);
  1940. adtsExtensionSampleingIndex = adtsSampleingIndex;
  1941. } else {
  1942. /* for other browsers (Chrome/Vivaldi/Opera ...)
  1943. always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)
  1944. */
  1945. adtsObjectType = 5;
  1946. config = new Array(4);
  1947. // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz)
  1948. if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) {
  1949. // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
  1950. // there is a factor 2 between frame sample rate and output sample rate
  1951. // multiply frequency by 2 (see table below, equivalent to substract 3)
  1952. adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
  1953. } else {
  1954. // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio)
  1955. // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo.
  1956. if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChanelConfig === 1) {
  1957. adtsObjectType = 2;
  1958. config = new Array(2);
  1959. }
  1960. adtsExtensionSampleingIndex = adtsSampleingIndex;
  1961. }
  1962. }
  1963. /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config
  1964. ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig()
  1965. Audio Profile / Audio Object Type
  1966. 0: Null
  1967. 1: AAC Main
  1968. 2: AAC LC (Low Complexity)
  1969. 3: AAC SSR (Scalable Sample Rate)
  1970. 4: AAC LTP (Long Term Prediction)
  1971. 5: SBR (Spectral Band Replication)
  1972. 6: AAC Scalable
  1973. sampling freq
  1974. 0: 96000 Hz
  1975. 1: 88200 Hz
  1976. 2: 64000 Hz
  1977. 3: 48000 Hz
  1978. 4: 44100 Hz
  1979. 5: 32000 Hz
  1980. 6: 24000 Hz
  1981. 7: 22050 Hz
  1982. 8: 16000 Hz
  1983. 9: 12000 Hz
  1984. 10: 11025 Hz
  1985. 11: 8000 Hz
  1986. 12: 7350 Hz
  1987. 13: Reserved
  1988. 14: Reserved
  1989. 15: frequency is written explictly
  1990. Channel Configurations
  1991. These are the channel configurations:
  1992. 0: Defined in AOT Specifc Config
  1993. 1: 1 channel: front-center
  1994. 2: 2 channels: front-left, front-right
  1995. */
  1996. // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1
  1997. config[0] = adtsObjectType << 3;
  1998. // samplingFrequencyIndex
  1999. config[0] |= (adtsSampleingIndex & 0x0E) >> 1;
  2000. config[1] |= (adtsSampleingIndex & 0x01) << 7;
  2001. // channelConfiguration
  2002. config[1] |= adtsChanelConfig << 3;
  2003. if (adtsObjectType === 5) {
  2004. // adtsExtensionSampleingIndex
  2005. config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1;
  2006. config[2] = (adtsExtensionSampleingIndex & 0x01) << 7;
  2007. // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???
  2008. // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc
  2009. config[2] |= 2 << 2;
  2010. config[3] = 0;
  2011. }
  2012. return {
  2013. config: config,
  2014. samplerate: adtsSampleingRates[adtsSampleingIndex],
  2015. channelCount: adtsChanelConfig,
  2016. codec: 'mp4a.40.' + adtsObjectType,
  2017. manifestCodec: manifestCodec
  2018. };
  2019. }
  2020. function isHeaderPattern(data, offset) {
  2021. return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0;
  2022. }
  2023. function getHeaderLength(data, offset) {
  2024. return !!(data[offset + 1] & 0x01) ? 7 : 9;
  2025. }
  2026. function getFullFrameLength(data, offset) {
  2027. return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5;
  2028. }
  2029. function isHeader(data, offset) {
  2030. // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
  2031. // Layer bits (position 14 and 15) in header should be always 0 for ADTS
  2032. // More info https://wiki.multimedia.cx/index.php?title=ADTS
  2033. if (offset + 1 < data.length && isHeaderPattern(data, offset)) {
  2034. return true;
  2035. }
  2036. return false;
  2037. }
  2038. function adts_probe(data, offset) {
  2039. // same as isHeader but we also check that ADTS frame follows last ADTS frame
  2040. // or end of data is reached
  2041. if (offset + 1 < data.length && isHeaderPattern(data, offset)) {
  2042. // ADTS header Length
  2043. var headerLength = getHeaderLength(data, offset);
  2044. // ADTS frame Length
  2045. var frameLength = headerLength;
  2046. if (offset + 5 < data.length) {
  2047. frameLength = getFullFrameLength(data, offset);
  2048. }
  2049. var newOffset = offset + frameLength;
  2050. if (newOffset === data.length || newOffset + 1 < data.length && isHeaderPattern(data, newOffset)) {
  2051. return true;
  2052. }
  2053. }
  2054. return false;
  2055. }
  2056. function initTrackConfig(track, observer, data, offset, audioCodec) {
  2057. if (!track.samplerate) {
  2058. var config = getAudioConfig(observer, data, offset, audioCodec);
  2059. track.config = config.config;
  2060. track.samplerate = config.samplerate;
  2061. track.channelCount = config.channelCount;
  2062. track.codec = config.codec;
  2063. track.manifestCodec = config.manifestCodec;
  2064. logger["b" /* logger */].log('parsed codec:' + track.codec + ',rate:' + config.samplerate + ',nb channel:' + config.channelCount);
  2065. }
  2066. }
  2067. function getFrameDuration(samplerate) {
  2068. return 1024 * 90000 / samplerate;
  2069. }
  2070. function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) {
  2071. var headerLength, frameLength, stamp;
  2072. var length = data.length;
  2073. // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
  2074. headerLength = getHeaderLength(data, offset);
  2075. // retrieve frame size
  2076. frameLength = getFullFrameLength(data, offset);
  2077. frameLength -= headerLength;
  2078. if (frameLength > 0 && offset + headerLength + frameLength <= length) {
  2079. stamp = pts + frameIndex * frameDuration;
  2080. //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
  2081. return {headerLength: headerLength, frameLength: frameLength, stamp: stamp};
  2082. }
  2083. return undefined;
  2084. }
  2085. function appendFrame(track, data, offset, pts, frameIndex) {
  2086. var frameDuration = getFrameDuration(track.samplerate);
  2087. var header = parseFrameHeader(data, offset, pts, frameIndex, frameDuration);
  2088. if (header) {
  2089. var stamp = header.stamp;
  2090. var headerLength = header.headerLength;
  2091. var frameLength = header.frameLength;
  2092. //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
  2093. var aacSample = {
  2094. unit: data.subarray(offset + headerLength, offset + headerLength + frameLength),
  2095. pts: stamp,
  2096. dts: stamp
  2097. };
  2098. track.samples.push(aacSample);
  2099. track.len += frameLength;
  2100. return {sample: aacSample, length: frameLength + headerLength};
  2101. }
  2102. return undefined;
  2103. }
  2104. // EXTERNAL MODULE: ./src/demux/id3.js
  2105. var id3 = __webpack_require__(4);
  2106. // CONCATENATED MODULE: ./src/demux/aacdemuxer.js
  2107. function _classCallCheck(instance, Constructor) {
  2108. if (!(instance instanceof Constructor)) {
  2109. throw new TypeError("Cannot call a class as a function");
  2110. }
  2111. }
  2112. /**
  2113. * AAC demuxer
  2114. */
  2115. var aacdemuxer_AACDemuxer = function () {
  2116. function AACDemuxer(observer, remuxer, config) {
  2117. _classCallCheck(this, AACDemuxer);
  2118. this.observer = observer;
  2119. this.config = config;
  2120. this.remuxer = remuxer;
  2121. }
  2122. AACDemuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
  2123. this._audioTrack = {
  2124. container: 'audio/adts',
  2125. type: 'audio',
  2126. id: 0,
  2127. sequenceNumber: 0,
  2128. isAAC: true,
  2129. samples: [],
  2130. len: 0,
  2131. manifestCodec: audioCodec,
  2132. duration: duration,
  2133. inputTimeScale: 90000
  2134. };
  2135. };
  2136. AACDemuxer.prototype.resetTimeStamp = function resetTimeStamp() {
  2137. };
  2138. AACDemuxer.probe = function probe(data) {
  2139. if (!data) {
  2140. return false;
  2141. }
  2142. // Check for the ADTS sync word
  2143. // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
  2144. // Layer bits (position 14 and 15) in header should be always 0 for ADTS
  2145. // More info https://wiki.multimedia.cx/index.php?title=ADTS
  2146. var id3Data = id3["a" /* default */].getID3Data(data, 0) || [];
  2147. var offset = id3Data.length;
  2148. for (var length = data.length; offset < length; offset++) {
  2149. if (adts_probe(data, offset)) {
  2150. logger["b" /* logger */].log('ADTS sync word found !');
  2151. return true;
  2152. }
  2153. }
  2154. return false;
  2155. };
  2156. // feed incoming data to the front of the parsing pipeline
  2157. AACDemuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
  2158. var track = this._audioTrack;
  2159. var id3Data = id3["a" /* default */].getID3Data(data, 0) || [];
  2160. var timestamp = id3["a" /* default */].getTimeStamp(id3Data);
  2161. var pts = timestamp ? 90 * timestamp : timeOffset * 90000;
  2162. var frameIndex = 0;
  2163. var stamp = pts;
  2164. var length = data.length;
  2165. var offset = id3Data.length;
  2166. var id3Samples = [{pts: stamp, dts: stamp, data: id3Data}];
  2167. while (offset < length - 1) {
  2168. if (isHeader(data, offset) && offset + 5 < length) {
  2169. initTrackConfig(track, this.observer, data, offset, track.manifestCodec);
  2170. var frame = appendFrame(track, data, offset, pts, frameIndex);
  2171. if (frame) {
  2172. offset += frame.length;
  2173. stamp = frame.sample.pts;
  2174. frameIndex++;
  2175. } else {
  2176. logger["b" /* logger */].log('Unable to parse AAC frame');
  2177. break;
  2178. }
  2179. } else if (id3["a" /* default */].isHeader(data, offset)) {
  2180. id3Data = id3["a" /* default */].getID3Data(data, offset);
  2181. id3Samples.push({pts: stamp, dts: stamp, data: id3Data});
  2182. offset += id3Data.length;
  2183. } else {
  2184. //nothing found, keep looking
  2185. offset++;
  2186. }
  2187. }
  2188. this.remuxer.remux(track, {samples: []}, {
  2189. samples: id3Samples,
  2190. inputTimeScale: 90000
  2191. }, {samples: []}, timeOffset, contiguous, accurateTimeOffset);
  2192. };
  2193. AACDemuxer.prototype.destroy = function destroy() {
  2194. };
  2195. return AACDemuxer;
  2196. }();
  2197. /* harmony default export */
  2198. var aacdemuxer = (aacdemuxer_AACDemuxer);
  2199. // EXTERNAL MODULE: ./src/demux/mp4demuxer.js
  2200. var mp4demuxer = __webpack_require__(7);
  2201. // CONCATENATED MODULE: ./src/demux/mpegaudio.js
  2202. /**
  2203. * MPEG parser helper
  2204. */
  2205. var MpegAudio = {
  2206. BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
  2207. SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000],
  2208. SamplesCoefficients: [
  2209. // MPEG 2.5
  2210. [0, // Reserved
  2211. 72, // Layer3
  2212. 144, // Layer2
  2213. 12 // Layer1
  2214. ],
  2215. // Reserved
  2216. [0, // Reserved
  2217. 0, // Layer3
  2218. 0, // Layer2
  2219. 0 // Layer1
  2220. ],
  2221. // MPEG 2
  2222. [0, // Reserved
  2223. 72, // Layer3
  2224. 144, // Layer2
  2225. 12 // Layer1
  2226. ],
  2227. // MPEG 1
  2228. [0, // Reserved
  2229. 144, // Layer3
  2230. 144, // Layer2
  2231. 12 // Layer1
  2232. ]],
  2233. BytesInSlot: [0, // Reserved
  2234. 1, // Layer3
  2235. 1, // Layer2
  2236. 4 // Layer1
  2237. ],
  2238. appendFrame: function appendFrame(track, data, offset, pts, frameIndex) {
  2239. // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
  2240. if (offset + 24 > data.length) {
  2241. return undefined;
  2242. }
  2243. var header = this.parseHeader(data, offset);
  2244. if (header && offset + header.frameLength <= data.length) {
  2245. var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate;
  2246. var stamp = pts + frameIndex * frameDuration;
  2247. var sample = {
  2248. unit: data.subarray(offset, offset + header.frameLength),
  2249. pts: stamp,
  2250. dts: stamp
  2251. };
  2252. track.config = [];
  2253. track.channelCount = header.channelCount;
  2254. track.samplerate = header.sampleRate;
  2255. track.samples.push(sample);
  2256. track.len += header.frameLength;
  2257. return {sample: sample, length: header.frameLength};
  2258. }
  2259. return undefined;
  2260. },
  2261. parseHeader: function parseHeader(data, offset) {
  2262. var headerB = data[offset + 1] >> 3 & 3;
  2263. var headerC = data[offset + 1] >> 1 & 3;
  2264. var headerE = data[offset + 2] >> 4 & 15;
  2265. var headerF = data[offset + 2] >> 2 & 3;
  2266. var headerG = data[offset + 2] >> 1 & 1;
  2267. if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) {
  2268. var columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4;
  2269. var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000;
  2270. var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2;
  2271. var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF];
  2272. var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)
  2273. var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC];
  2274. var bytesInSlot = MpegAudio.BytesInSlot[headerC];
  2275. var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot;
  2276. var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot;
  2277. return {
  2278. sampleRate: sampleRate,
  2279. channelCount: channelCount,
  2280. frameLength: frameLength,
  2281. samplesPerFrame: samplesPerFrame
  2282. };
  2283. }
  2284. return undefined;
  2285. },
  2286. isHeaderPattern: function isHeaderPattern(data, offset) {
  2287. return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;
  2288. },
  2289. isHeader: function isHeader(data, offset) {
  2290. // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
  2291. // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
  2292. // More info http://www.mp3-tech.org/programmer/frame_header.html
  2293. if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
  2294. return true;
  2295. }
  2296. return false;
  2297. },
  2298. probe: function probe(data, offset) {
  2299. // same as isHeader but we also check that MPEG frame follows last MPEG frame
  2300. // or end of data is reached
  2301. if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
  2302. // MPEG header Length
  2303. var headerLength = 4;
  2304. // MPEG frame Length
  2305. var header = this.parseHeader(data, offset);
  2306. var frameLength = headerLength;
  2307. if (header && header.frameLength) {
  2308. frameLength = header.frameLength;
  2309. }
  2310. var newOffset = offset + frameLength;
  2311. if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) {
  2312. return true;
  2313. }
  2314. }
  2315. return false;
  2316. }
  2317. };
  2318. /* harmony default export */
  2319. var mpegaudio = (MpegAudio);
  2320. // CONCATENATED MODULE: ./src/demux/exp-golomb.js
  2321. function exp_golomb__classCallCheck(instance, Constructor) {
  2322. if (!(instance instanceof Constructor)) {
  2323. throw new TypeError("Cannot call a class as a function");
  2324. }
  2325. }
  2326. /**
  2327. * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
  2328. */
  2329. var exp_golomb_ExpGolomb = function () {
  2330. function ExpGolomb(data) {
  2331. exp_golomb__classCallCheck(this, ExpGolomb);
  2332. this.data = data;
  2333. // the number of bytes left to examine in this.data
  2334. this.bytesAvailable = data.byteLength;
  2335. // the current word being examined
  2336. this.word = 0; // :uint
  2337. // the number of bits left to examine in the current word
  2338. this.bitsAvailable = 0; // :uint
  2339. }
  2340. // ():void
  2341. ExpGolomb.prototype.loadWord = function loadWord() {
  2342. var data = this.data,
  2343. bytesAvailable = this.bytesAvailable,
  2344. position = data.byteLength - bytesAvailable,
  2345. workingBytes = new Uint8Array(4),
  2346. availableBytes = Math.min(4, bytesAvailable);
  2347. if (availableBytes === 0) {
  2348. throw new Error('no bytes available');
  2349. }
  2350. workingBytes.set(data.subarray(position, position + availableBytes));
  2351. this.word = new DataView(workingBytes.buffer).getUint32(0);
  2352. // track the amount of this.data that has been processed
  2353. this.bitsAvailable = availableBytes * 8;
  2354. this.bytesAvailable -= availableBytes;
  2355. };
  2356. // (count:int):void
  2357. ExpGolomb.prototype.skipBits = function skipBits(count) {
  2358. var skipBytes; // :int
  2359. if (this.bitsAvailable > count) {
  2360. this.word <<= count;
  2361. this.bitsAvailable -= count;
  2362. } else {
  2363. count -= this.bitsAvailable;
  2364. skipBytes = count >> 3;
  2365. count -= skipBytes >> 3;
  2366. this.bytesAvailable -= skipBytes;
  2367. this.loadWord();
  2368. this.word <<= count;
  2369. this.bitsAvailable -= count;
  2370. }
  2371. };
  2372. // (size:int):uint
  2373. ExpGolomb.prototype.readBits = function readBits(size) {
  2374. var bits = Math.min(this.bitsAvailable, size),
  2375. // :uint
  2376. valu = this.word >>> 32 - bits; // :uint
  2377. if (size > 32) {
  2378. logger["b" /* logger */].error('Cannot read more than 32 bits at a time');
  2379. }
  2380. this.bitsAvailable -= bits;
  2381. if (this.bitsAvailable > 0) {
  2382. this.word <<= bits;
  2383. } else if (this.bytesAvailable > 0) {
  2384. this.loadWord();
  2385. }
  2386. bits = size - bits;
  2387. if (bits > 0 && this.bitsAvailable) {
  2388. return valu << bits | this.readBits(bits);
  2389. } else {
  2390. return valu;
  2391. }
  2392. };
  2393. // ():uint
  2394. ExpGolomb.prototype.skipLZ = function skipLZ() {
  2395. var leadingZeroCount; // :uint
  2396. for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) {
  2397. if (0 !== (this.word & 0x80000000 >>> leadingZeroCount)) {
  2398. // the first bit of working word is 1
  2399. this.word <<= leadingZeroCount;
  2400. this.bitsAvailable -= leadingZeroCount;
  2401. return leadingZeroCount;
  2402. }
  2403. }
  2404. // we exhausted word and still have not found a 1
  2405. this.loadWord();
  2406. return leadingZeroCount + this.skipLZ();
  2407. };
  2408. // ():void
  2409. ExpGolomb.prototype.skipUEG = function skipUEG() {
  2410. this.skipBits(1 + this.skipLZ());
  2411. };
  2412. // ():void
  2413. ExpGolomb.prototype.skipEG = function skipEG() {
  2414. this.skipBits(1 + this.skipLZ());
  2415. };
  2416. // ():uint
  2417. ExpGolomb.prototype.readUEG = function readUEG() {
  2418. var clz = this.skipLZ(); // :uint
  2419. return this.readBits(clz + 1) - 1;
  2420. };
  2421. // ():int
  2422. ExpGolomb.prototype.readEG = function readEG() {
  2423. var valu = this.readUEG(); // :int
  2424. if (0x01 & valu) {
  2425. // the number is odd if the low order bit is set
  2426. return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
  2427. } else {
  2428. return -1 * (valu >>> 1); // divide by two then make it negative
  2429. }
  2430. };
  2431. // Some convenience functions
  2432. // :Boolean
  2433. ExpGolomb.prototype.readBoolean = function readBoolean() {
  2434. return 1 === this.readBits(1);
  2435. };
  2436. // ():int
  2437. ExpGolomb.prototype.readUByte = function readUByte() {
  2438. return this.readBits(8);
  2439. };
  2440. // ():int
  2441. ExpGolomb.prototype.readUShort = function readUShort() {
  2442. return this.readBits(16);
  2443. };
  2444. // ():int
  2445. ExpGolomb.prototype.readUInt = function readUInt() {
  2446. return this.readBits(32);
  2447. };
  2448. /**
  2449. * Advance the ExpGolomb decoder past a scaling list. The scaling
  2450. * list is optionally transmitted as part of a sequence parameter
  2451. * set and is not relevant to transmuxing.
  2452. * @param count {number} the number of entries in this scaling list
  2453. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  2454. */
  2455. ExpGolomb.prototype.skipScalingList = function skipScalingList(count) {
  2456. var lastScale = 8,
  2457. nextScale = 8,
  2458. j,
  2459. deltaScale;
  2460. for (j = 0; j < count; j++) {
  2461. if (nextScale !== 0) {
  2462. deltaScale = this.readEG();
  2463. nextScale = (lastScale + deltaScale + 256) % 256;
  2464. }
  2465. lastScale = nextScale === 0 ? lastScale : nextScale;
  2466. }
  2467. };
  2468. /**
  2469. * Read a sequence parameter set and return some interesting video
  2470. * properties. A sequence parameter set is the H264 metadata that
  2471. * describes the properties of upcoming video frames.
  2472. * @param data {Uint8Array} the bytes of a sequence parameter set
  2473. * @return {object} an object with configuration parsed from the
  2474. * sequence parameter set, including the dimensions of the
  2475. * associated video frames.
  2476. */
  2477. ExpGolomb.prototype.readSPS = function readSPS() {
  2478. var frameCropLeftOffset = 0,
  2479. frameCropRightOffset = 0,
  2480. frameCropTopOffset = 0,
  2481. frameCropBottomOffset = 0,
  2482. profileIdc,
  2483. profileCompat,
  2484. levelIdc,
  2485. numRefFramesInPicOrderCntCycle,
  2486. picWidthInMbsMinus1,
  2487. picHeightInMapUnitsMinus1,
  2488. frameMbsOnlyFlag,
  2489. scalingListCount,
  2490. i,
  2491. readUByte = this.readUByte.bind(this),
  2492. readBits = this.readBits.bind(this),
  2493. readUEG = this.readUEG.bind(this),
  2494. readBoolean = this.readBoolean.bind(this),
  2495. skipBits = this.skipBits.bind(this),
  2496. skipEG = this.skipEG.bind(this),
  2497. skipUEG = this.skipUEG.bind(this),
  2498. skipScalingList = this.skipScalingList.bind(this);
  2499. readUByte();
  2500. profileIdc = readUByte(); // profile_idc
  2501. profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5)
  2502. skipBits(3); // reserved_zero_3bits u(3),
  2503. levelIdc = readUByte(); //level_idc u(8)
  2504. skipUEG(); // seq_parameter_set_id
  2505. // some profiles have more optional data we don't need
  2506. if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
  2507. var chromaFormatIdc = readUEG();
  2508. if (chromaFormatIdc === 3) {
  2509. skipBits(1); // separate_colour_plane_flag
  2510. }
  2511. skipUEG(); // bit_depth_luma_minus8
  2512. skipUEG(); // bit_depth_chroma_minus8
  2513. skipBits(1); // qpprime_y_zero_transform_bypass_flag
  2514. if (readBoolean()) {
  2515. // seq_scaling_matrix_present_flag
  2516. scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
  2517. for (i = 0; i < scalingListCount; i++) {
  2518. if (readBoolean()) {
  2519. // seq_scaling_list_present_flag[ i ]
  2520. if (i < 6) {
  2521. skipScalingList(16);
  2522. } else {
  2523. skipScalingList(64);
  2524. }
  2525. }
  2526. }
  2527. }
  2528. }
  2529. skipUEG(); // log2_max_frame_num_minus4
  2530. var picOrderCntType = readUEG();
  2531. if (picOrderCntType === 0) {
  2532. readUEG(); //log2_max_pic_order_cnt_lsb_minus4
  2533. } else if (picOrderCntType === 1) {
  2534. skipBits(1); // delta_pic_order_always_zero_flag
  2535. skipEG(); // offset_for_non_ref_pic
  2536. skipEG(); // offset_for_top_to_bottom_field
  2537. numRefFramesInPicOrderCntCycle = readUEG();
  2538. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  2539. skipEG(); // offset_for_ref_frame[ i ]
  2540. }
  2541. }
  2542. skipUEG(); // max_num_ref_frames
  2543. skipBits(1); // gaps_in_frame_num_value_allowed_flag
  2544. picWidthInMbsMinus1 = readUEG();
  2545. picHeightInMapUnitsMinus1 = readUEG();
  2546. frameMbsOnlyFlag = readBits(1);
  2547. if (frameMbsOnlyFlag === 0) {
  2548. skipBits(1); // mb_adaptive_frame_field_flag
  2549. }
  2550. skipBits(1); // direct_8x8_inference_flag
  2551. if (readBoolean()) {
  2552. // frame_cropping_flag
  2553. frameCropLeftOffset = readUEG();
  2554. frameCropRightOffset = readUEG();
  2555. frameCropTopOffset = readUEG();
  2556. frameCropBottomOffset = readUEG();
  2557. }
  2558. var pixelRatio = [1, 1];
  2559. if (readBoolean()) {
  2560. // vui_parameters_present_flag
  2561. if (readBoolean()) {
  2562. // aspect_ratio_info_present_flag
  2563. var aspectRatioIdc = readUByte();
  2564. switch (aspectRatioIdc) {
  2565. case 1:
  2566. pixelRatio = [1, 1];
  2567. break;
  2568. case 2:
  2569. pixelRatio = [12, 11];
  2570. break;
  2571. case 3:
  2572. pixelRatio = [10, 11];
  2573. break;
  2574. case 4:
  2575. pixelRatio = [16, 11];
  2576. break;
  2577. case 5:
  2578. pixelRatio = [40, 33];
  2579. break;
  2580. case 6:
  2581. pixelRatio = [24, 11];
  2582. break;
  2583. case 7:
  2584. pixelRatio = [20, 11];
  2585. break;
  2586. case 8:
  2587. pixelRatio = [32, 11];
  2588. break;
  2589. case 9:
  2590. pixelRatio = [80, 33];
  2591. break;
  2592. case 10:
  2593. pixelRatio = [18, 11];
  2594. break;
  2595. case 11:
  2596. pixelRatio = [15, 11];
  2597. break;
  2598. case 12:
  2599. pixelRatio = [64, 33];
  2600. break;
  2601. case 13:
  2602. pixelRatio = [160, 99];
  2603. break;
  2604. case 14:
  2605. pixelRatio = [4, 3];
  2606. break;
  2607. case 15:
  2608. pixelRatio = [3, 2];
  2609. break;
  2610. case 16:
  2611. pixelRatio = [2, 1];
  2612. break;
  2613. case 255: {
  2614. pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
  2615. break;
  2616. }
  2617. }
  2618. }
  2619. }
  2620. return {
  2621. width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
  2622. height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
  2623. pixelRatio: pixelRatio
  2624. };
  2625. };
  2626. ExpGolomb.prototype.readSliceType = function readSliceType() {
  2627. // skip NALu type
  2628. this.readUByte();
  2629. // discard first_mb_in_slice
  2630. this.readUEG();
  2631. // return slice_type
  2632. return this.readUEG();
  2633. };
  2634. return ExpGolomb;
  2635. }();
  2636. /* harmony default export */
  2637. var exp_golomb = (exp_golomb_ExpGolomb);
  2638. // CONCATENATED MODULE: ./src/demux/sample-aes.js
  2639. function sample_aes__classCallCheck(instance, Constructor) {
  2640. if (!(instance instanceof Constructor)) {
  2641. throw new TypeError("Cannot call a class as a function");
  2642. }
  2643. }
  2644. /**
  2645. * SAMPLE-AES decrypter
  2646. */
  2647. var sample_aes_SampleAesDecrypter = function () {
  2648. function SampleAesDecrypter(observer, config, decryptdata, discardEPB) {
  2649. sample_aes__classCallCheck(this, SampleAesDecrypter);
  2650. this.decryptdata = decryptdata;
  2651. this.discardEPB = discardEPB;
  2652. this.decrypter = new crypt_decrypter["a" /* default */](observer, config);
  2653. }
  2654. SampleAesDecrypter.prototype.decryptBuffer = function decryptBuffer(encryptedData, callback) {
  2655. this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback);
  2656. };
  2657. // AAC - encrypt all full 16 bytes blocks starting from offset 16
  2658. SampleAesDecrypter.prototype.decryptAacSample = function decryptAacSample(samples, sampleIndex, callback, sync) {
  2659. var curUnit = samples[sampleIndex].unit;
  2660. var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16);
  2661. var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length);
  2662. var localthis = this;
  2663. this.decryptBuffer(encryptedBuffer, function (decryptedData) {
  2664. decryptedData = new Uint8Array(decryptedData);
  2665. curUnit.set(decryptedData, 16);
  2666. if (!sync) {
  2667. localthis.decryptAacSamples(samples, sampleIndex + 1, callback);
  2668. }
  2669. });
  2670. };
  2671. SampleAesDecrypter.prototype.decryptAacSamples = function decryptAacSamples(samples, sampleIndex, callback) {
  2672. for (; ; sampleIndex++) {
  2673. if (sampleIndex >= samples.length) {
  2674. callback();
  2675. return;
  2676. }
  2677. if (samples[sampleIndex].unit.length < 32) {
  2678. continue;
  2679. }
  2680. var sync = this.decrypter.isSync();
  2681. this.decryptAacSample(samples, sampleIndex, callback, sync);
  2682. if (!sync) {
  2683. return;
  2684. }
  2685. }
  2686. };
  2687. // AVC - encrypt one 16 bytes block out of ten, starting from offset 32
  2688. SampleAesDecrypter.prototype.getAvcEncryptedData = function getAvcEncryptedData(decodedData) {
  2689. var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16;
  2690. var encryptedData = new Int8Array(encryptedDataLen);
  2691. var outputPos = 0;
  2692. for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) {
  2693. encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos);
  2694. }
  2695. return encryptedData;
  2696. };
  2697. SampleAesDecrypter.prototype.getAvcDecryptedUnit = function getAvcDecryptedUnit(decodedData, decryptedData) {
  2698. decryptedData = new Uint8Array(decryptedData);
  2699. var inputPos = 0;
  2700. for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) {
  2701. decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos);
  2702. }
  2703. return decodedData;
  2704. };
  2705. SampleAesDecrypter.prototype.decryptAvcSample = function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync) {
  2706. var decodedData = this.discardEPB(curUnit.data);
  2707. var encryptedData = this.getAvcEncryptedData(decodedData);
  2708. var localthis = this;
  2709. this.decryptBuffer(encryptedData.buffer, function (decryptedData) {
  2710. curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData);
  2711. if (!sync) {
  2712. localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
  2713. }
  2714. });
  2715. };
  2716. SampleAesDecrypter.prototype.decryptAvcSamples = function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) {
  2717. for (; ; sampleIndex++, unitIndex = 0) {
  2718. if (sampleIndex >= samples.length) {
  2719. callback();
  2720. return;
  2721. }
  2722. var curUnits = samples[sampleIndex].units;
  2723. for (; ; unitIndex++) {
  2724. if (unitIndex >= curUnits.length) {
  2725. break;
  2726. }
  2727. var curUnit = curUnits[unitIndex];
  2728. if (curUnit.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) {
  2729. continue;
  2730. }
  2731. var sync = this.decrypter.isSync();
  2732. this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync);
  2733. if (!sync) {
  2734. return;
  2735. }
  2736. }
  2737. }
  2738. };
  2739. return SampleAesDecrypter;
  2740. }();
  2741. /* harmony default export */
  2742. var sample_aes = (sample_aes_SampleAesDecrypter);
  2743. // CONCATENATED MODULE: ./src/demux/tsdemuxer.js
  2744. function tsdemuxer__classCallCheck(instance, Constructor) {
  2745. if (!(instance instanceof Constructor)) {
  2746. throw new TypeError("Cannot call a class as a function");
  2747. }
  2748. }
  2749. /**
  2750. * highly optimized TS demuxer:
  2751. * parse PAT, PMT
  2752. * extract PES packet from audio and video PIDs
  2753. * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet
  2754. * trigger the remuxer upon parsing completion
  2755. * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource.
  2756. * it also controls the remuxing process :
  2757. * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state.
  2758. */
  2759. // import Hex from '../utils/hex';
  2760. // We are using fixed track IDs for driving the MP4 remuxer
  2761. // instead of following the TS PIDs.
  2762. // There is no reason not to do this and some browsers/SourceBuffer-demuxers
  2763. // may not like if there are TrackID "switches"
  2764. // See https://github.com/video-dev/hls.js/issues/1331
  2765. // Here we are mapping our internal track types to constant MP4 track IDs
  2766. // With MSE currently one can only have one track of each, and we are muxing
  2767. // whatever video/audio rendition in them.
  2768. var RemuxerTrackIdConfig = {
  2769. video: 0,
  2770. audio: 1,
  2771. id3: 2,
  2772. text: 3
  2773. };
  2774. var tsdemuxer_TSDemuxer = function () {
  2775. function TSDemuxer(observer, remuxer, config, typeSupported) {
  2776. tsdemuxer__classCallCheck(this, TSDemuxer);
  2777. this.observer = observer;
  2778. this.config = config;
  2779. this.typeSupported = typeSupported;
  2780. this.remuxer = remuxer;
  2781. this.sampleAes = null;
  2782. }
  2783. TSDemuxer.prototype.setDecryptData = function setDecryptData(decryptdata) {
  2784. if (decryptdata != null && decryptdata.key != null && decryptdata.method === 'SAMPLE-AES') {
  2785. this.sampleAes = new sample_aes(this.observer, this.config, decryptdata, this.discardEPB);
  2786. } else {
  2787. this.sampleAes = null;
  2788. }
  2789. };
  2790. TSDemuxer.probe = function probe(data) {
  2791. var syncOffset = TSDemuxer._syncOffset(data);
  2792. if (syncOffset < 0) {
  2793. return false;
  2794. } else {
  2795. if (syncOffset) {
  2796. logger["b" /* logger */].warn('MPEG2-TS detected but first sync word found @ offset ' + syncOffset + ', junk ahead ?');
  2797. }
  2798. return true;
  2799. }
  2800. };
  2801. TSDemuxer._syncOffset = function _syncOffset(data) {
  2802. // scan 1000 first bytes
  2803. var scanwindow = Math.min(1000, data.length - 3 * 188);
  2804. var i = 0;
  2805. while (i < scanwindow) {
  2806. // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47
  2807. if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) {
  2808. return i;
  2809. } else {
  2810. i++;
  2811. }
  2812. }
  2813. return -1;
  2814. };
  2815. /**
  2816. * Creates a track model internal to demuxer used to drive remuxing input
  2817. *
  2818. * @param {string} type 'audio' | 'video' | 'id3' | 'text'
  2819. * @param {number} duration
  2820. * @return {object} TSDemuxer's internal track model
  2821. */
  2822. TSDemuxer.createTrack = function createTrack(type, duration) {
  2823. return {
  2824. container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined,
  2825. type: type,
  2826. id: RemuxerTrackIdConfig[type],
  2827. pid: -1,
  2828. inputTimeScale: 90000,
  2829. sequenceNumber: 0,
  2830. samples: [],
  2831. len: 0,
  2832. dropped: type === 'video' ? 0 : undefined,
  2833. isAAC: type === 'audio' ? true : undefined,
  2834. duration: type === 'audio' ? duration : undefined
  2835. };
  2836. };
  2837. /**
  2838. * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start)
  2839. * Resets all internal track instances of the demuxer.
  2840. *
  2841. * @override Implements generic demuxing/remuxing interface (see DemuxerInline)
  2842. * @param {object} initSegment
  2843. * @param {string} audioCodec
  2844. * @param {string} videoCodec
  2845. * @param {number} duration (in TS timescale = 90kHz)
  2846. */
  2847. TSDemuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
  2848. this.pmtParsed = false;
  2849. this._pmtId = -1;
  2850. this._avcTrack = TSDemuxer.createTrack('video', duration);
  2851. this._audioTrack = TSDemuxer.createTrack('audio', duration);
  2852. this._id3Track = TSDemuxer.createTrack('id3', duration);
  2853. this._txtTrack = TSDemuxer.createTrack('text', duration);
  2854. // flush any partial content
  2855. this.aacOverFlow = null;
  2856. this.aacLastPTS = null;
  2857. this.avcSample = null;
  2858. this.audioCodec = audioCodec;
  2859. this.videoCodec = videoCodec;
  2860. this._duration = duration;
  2861. };
  2862. /**
  2863. *
  2864. * @override
  2865. */
  2866. TSDemuxer.prototype.resetTimeStamp = function resetTimeStamp() {
  2867. };
  2868. // feed incoming data to the front of the parsing pipeline
  2869. TSDemuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
  2870. var start,
  2871. len = data.length,
  2872. stt,
  2873. pid,
  2874. atf,
  2875. offset,
  2876. pes,
  2877. unknownPIDs = false;
  2878. this.contiguous = contiguous;
  2879. var pmtParsed = this.pmtParsed,
  2880. avcTrack = this._avcTrack,
  2881. audioTrack = this._audioTrack,
  2882. id3Track = this._id3Track,
  2883. avcId = avcTrack.pid,
  2884. audioId = audioTrack.pid,
  2885. id3Id = id3Track.pid,
  2886. pmtId = this._pmtId,
  2887. avcData = avcTrack.pesData,
  2888. audioData = audioTrack.pesData,
  2889. id3Data = id3Track.pesData,
  2890. parsePAT = this._parsePAT,
  2891. parsePMT = this._parsePMT,
  2892. parsePES = this._parsePES,
  2893. parseAVCPES = this._parseAVCPES.bind(this),
  2894. parseAACPES = this._parseAACPES.bind(this),
  2895. parseMPEGPES = this._parseMPEGPES.bind(this),
  2896. parseID3PES = this._parseID3PES.bind(this);
  2897. var syncOffset = TSDemuxer._syncOffset(data);
  2898. // don't parse last TS packet if incomplete
  2899. len -= (len + syncOffset) % 188;
  2900. // loop through TS packets
  2901. for (start = syncOffset; start < len; start += 188) {
  2902. if (data[start] === 0x47) {
  2903. stt = !!(data[start + 1] & 0x40);
  2904. // pid is a 13-bit field starting at the last bit of TS[1]
  2905. pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
  2906. atf = (data[start + 3] & 0x30) >> 4;
  2907. // if an adaption field is present, its length is specified by the fifth byte of the TS packet header.
  2908. if (atf > 1) {
  2909. offset = start + 5 + data[start + 4];
  2910. // continue if there is only adaptation field
  2911. if (offset === start + 188) {
  2912. continue;
  2913. }
  2914. } else {
  2915. offset = start + 4;
  2916. }
  2917. switch (pid) {
  2918. case avcId:
  2919. if (stt) {
  2920. if (avcData && (pes = parsePES(avcData))) {
  2921. parseAVCPES(pes, false);
  2922. }
  2923. avcData = {data: [], size: 0};
  2924. }
  2925. if (avcData) {
  2926. avcData.data.push(data.subarray(offset, start + 188));
  2927. avcData.size += start + 188 - offset;
  2928. }
  2929. break;
  2930. case audioId:
  2931. if (stt) {
  2932. if (audioData && (pes = parsePES(audioData))) {
  2933. if (audioTrack.isAAC) {
  2934. parseAACPES(pes);
  2935. } else {
  2936. parseMPEGPES(pes);
  2937. }
  2938. }
  2939. audioData = {data: [], size: 0};
  2940. }
  2941. if (audioData) {
  2942. audioData.data.push(data.subarray(offset, start + 188));
  2943. audioData.size += start + 188 - offset;
  2944. }
  2945. break;
  2946. case id3Id:
  2947. if (stt) {
  2948. if (id3Data && (pes = parsePES(id3Data))) {
  2949. parseID3PES(pes);
  2950. }
  2951. id3Data = {data: [], size: 0};
  2952. }
  2953. if (id3Data) {
  2954. id3Data.data.push(data.subarray(offset, start + 188));
  2955. id3Data.size += start + 188 - offset;
  2956. }
  2957. break;
  2958. case 0:
  2959. if (stt) {
  2960. offset += data[offset] + 1;
  2961. }
  2962. pmtId = this._pmtId = parsePAT(data, offset);
  2963. break;
  2964. case pmtId:
  2965. if (stt) {
  2966. offset += data[offset] + 1;
  2967. }
  2968. var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null);
  2969. // only update track id if track PID found while parsing PMT
  2970. // this is to avoid resetting the PID to -1 in case
  2971. // track PID transiently disappears from the stream
  2972. // this could happen in case of transient missing audio samples for example
  2973. // NOTE this is only the PID of the track as found in TS,
  2974. // but we are not using this for MP4 track IDs.
  2975. avcId = parsedPIDs.avc;
  2976. if (avcId > 0) {
  2977. avcTrack.pid = avcId;
  2978. }
  2979. audioId = parsedPIDs.audio;
  2980. if (audioId > 0) {
  2981. audioTrack.pid = audioId;
  2982. audioTrack.isAAC = parsedPIDs.isAAC;
  2983. }
  2984. id3Id = parsedPIDs.id3;
  2985. if (id3Id > 0) {
  2986. id3Track.pid = id3Id;
  2987. }
  2988. if (unknownPIDs && !pmtParsed) {
  2989. logger["b" /* logger */].log('reparse from beginning');
  2990. unknownPIDs = false;
  2991. // we set it to -188, the += 188 in the for loop will reset start to 0
  2992. start = syncOffset - 188;
  2993. }
  2994. pmtParsed = this.pmtParsed = true;
  2995. break;
  2996. case 17:
  2997. case 0x1fff:
  2998. break;
  2999. default:
  3000. unknownPIDs = true;
  3001. break;
  3002. }
  3003. } else {
  3004. this.observer.trigger(events["a" /* default */].ERROR, {
  3005. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  3006. details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR,
  3007. fatal: false,
  3008. reason: 'TS packet did not start with 0x47'
  3009. });
  3010. }
  3011. }
  3012. // try to parse last PES packets
  3013. if (avcData && (pes = parsePES(avcData))) {
  3014. parseAVCPES(pes, true);
  3015. avcTrack.pesData = null;
  3016. } else {
  3017. // either avcData null or PES truncated, keep it for next frag parsing
  3018. avcTrack.pesData = avcData;
  3019. }
  3020. if (audioData && (pes = parsePES(audioData))) {
  3021. if (audioTrack.isAAC) {
  3022. parseAACPES(pes);
  3023. } else {
  3024. parseMPEGPES(pes);
  3025. }
  3026. audioTrack.pesData = null;
  3027. } else {
  3028. if (audioData && audioData.size) {
  3029. logger["b" /* logger */].log('last AAC PES packet truncated,might overlap between fragments');
  3030. }
  3031. // either audioData null or PES truncated, keep it for next frag parsing
  3032. audioTrack.pesData = audioData;
  3033. }
  3034. if (id3Data && (pes = parsePES(id3Data))) {
  3035. parseID3PES(pes);
  3036. id3Track.pesData = null;
  3037. } else {
  3038. // either id3Data null or PES truncated, keep it for next frag parsing
  3039. id3Track.pesData = id3Data;
  3040. }
  3041. if (this.sampleAes == null) {
  3042. this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
  3043. } else {
  3044. this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
  3045. }
  3046. };
  3047. TSDemuxer.prototype.decryptAndRemux = function decryptAndRemux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
  3048. if (audioTrack.samples && audioTrack.isAAC) {
  3049. var localthis = this;
  3050. this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () {
  3051. localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  3052. });
  3053. } else {
  3054. this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  3055. }
  3056. };
  3057. TSDemuxer.prototype.decryptAndRemuxAvc = function decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
  3058. if (videoTrack.samples) {
  3059. var localthis = this;
  3060. this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () {
  3061. localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  3062. });
  3063. } else {
  3064. this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  3065. }
  3066. };
  3067. TSDemuxer.prototype.destroy = function destroy() {
  3068. this._initPTS = this._initDTS = undefined;
  3069. this._duration = 0;
  3070. };
  3071. TSDemuxer.prototype._parsePAT = function _parsePAT(data, offset) {
  3072. // skip the PSI header and parse the first PMT entry
  3073. return (data[offset + 10] & 0x1F) << 8 | data[offset + 11];
  3074. //logger.log('PMT PID:' + this._pmtId);
  3075. };
  3076. TSDemuxer.prototype._parsePMT = function _parsePMT(data, offset, mpegSupported, isSampleAes) {
  3077. var sectionLength,
  3078. tableEnd,
  3079. programInfoLength,
  3080. pid,
  3081. result = {audio: -1, avc: -1, id3: -1, isAAC: true};
  3082. sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2];
  3083. tableEnd = offset + 3 + sectionLength - 4;
  3084. // to determine where the table is, we have to figure out how
  3085. // long the program info descriptors are
  3086. programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11];
  3087. // advance the offset to the first entry in the mapping table
  3088. offset += 12 + programInfoLength;
  3089. while (offset < tableEnd) {
  3090. pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2];
  3091. switch (data[offset]) {
  3092. case 0xcf:
  3093. // SAMPLE-AES AAC
  3094. if (!isSampleAes) {
  3095. logger["b" /* logger */].log('unkown stream type:' + data[offset]);
  3096. break;
  3097. }
  3098. /* falls through */
  3099. // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
  3100. case 0x0f:
  3101. //logger.log('AAC PID:' + pid);
  3102. if (result.audio === -1) {
  3103. result.audio = pid;
  3104. }
  3105. break;
  3106. // Packetized metadata (ID3)
  3107. case 0x15:
  3108. //logger.log('ID3 PID:' + pid);
  3109. if (result.id3 === -1) {
  3110. result.id3 = pid;
  3111. }
  3112. break;
  3113. case 0xdb:
  3114. // SAMPLE-AES AVC
  3115. if (!isSampleAes) {
  3116. logger["b" /* logger */].log('unkown stream type:' + data[offset]);
  3117. break;
  3118. }
  3119. /* falls through */
  3120. // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
  3121. case 0x1b:
  3122. //logger.log('AVC PID:' + pid);
  3123. if (result.avc === -1) {
  3124. result.avc = pid;
  3125. }
  3126. break;
  3127. // ISO/IEC 11172-3 (MPEG-1 audio)
  3128. // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
  3129. case 0x03:
  3130. case 0x04:
  3131. //logger.log('MPEG PID:' + pid);
  3132. if (!mpegSupported) {
  3133. logger["b" /* logger */].log('MPEG audio found, not supported in this browser for now');
  3134. } else if (result.audio === -1) {
  3135. result.audio = pid;
  3136. result.isAAC = false;
  3137. }
  3138. break;
  3139. case 0x24:
  3140. logger["b" /* logger */].warn('HEVC stream type found, not supported for now');
  3141. break;
  3142. default:
  3143. logger["b" /* logger */].log('unkown stream type:' + data[offset]);
  3144. break;
  3145. }
  3146. // move to the next table entry
  3147. // skip past the elementary stream descriptors, if present
  3148. offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5;
  3149. }
  3150. return result;
  3151. };
  3152. TSDemuxer.prototype._parsePES = function _parsePES(stream) {
  3153. var i = 0,
  3154. frag,
  3155. pesFlags,
  3156. pesPrefix,
  3157. pesLen,
  3158. pesHdrLen,
  3159. pesData,
  3160. pesPts,
  3161. pesDts,
  3162. payloadStartOffset,
  3163. data = stream.data;
  3164. // safety check
  3165. if (!stream || stream.size === 0) {
  3166. return null;
  3167. }
  3168. // we might need up to 19 bytes to read PES header
  3169. // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes
  3170. // usually only one merge is needed (and this is rare ...)
  3171. while (data[0].length < 19 && data.length > 1) {
  3172. var newData = new Uint8Array(data[0].length + data[1].length);
  3173. newData.set(data[0]);
  3174. newData.set(data[1], data[0].length);
  3175. data[0] = newData;
  3176. data.splice(1, 1);
  3177. }
  3178. //retrieve PTS/DTS from first fragment
  3179. frag = data[0];
  3180. pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2];
  3181. if (pesPrefix === 1) {
  3182. pesLen = (frag[4] << 8) + frag[5];
  3183. // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated
  3184. // minus 6 : PES header size
  3185. if (pesLen && pesLen > stream.size - 6) {
  3186. return null;
  3187. }
  3188. pesFlags = frag[7];
  3189. if (pesFlags & 0xC0) {
  3190. /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  3191. as PTS / DTS is 33 bit we cannot use bitwise operator in JS,
  3192. as Bitwise operators treat their operands as a sequence of 32 bits */
  3193. pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29
  3194. (frag[10] & 0xFF) * 4194304 + // 1 << 22
  3195. (frag[11] & 0xFE) * 16384 + // 1 << 14
  3196. (frag[12] & 0xFF) * 128 + // 1 << 7
  3197. (frag[13] & 0xFE) / 2;
  3198. // check if greater than 2^32 -1
  3199. if (pesPts > 4294967295) {
  3200. // decrement 2^33
  3201. pesPts -= 8589934592;
  3202. }
  3203. if (pesFlags & 0x40) {
  3204. pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29
  3205. (frag[15] & 0xFF) * 4194304 + // 1 << 22
  3206. (frag[16] & 0xFE) * 16384 + // 1 << 14
  3207. (frag[17] & 0xFF) * 128 + // 1 << 7
  3208. (frag[18] & 0xFE) / 2;
  3209. // check if greater than 2^32 -1
  3210. if (pesDts > 4294967295) {
  3211. // decrement 2^33
  3212. pesDts -= 8589934592;
  3213. }
  3214. if (pesPts - pesDts > 60 * 90000) {
  3215. logger["b" /* logger */].warn(Math.round((pesPts - pesDts) / 90000) + 's delta between PTS and DTS, align them');
  3216. pesPts = pesDts;
  3217. }
  3218. } else {
  3219. pesDts = pesPts;
  3220. }
  3221. }
  3222. pesHdrLen = frag[8];
  3223. // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension
  3224. payloadStartOffset = pesHdrLen + 9;
  3225. stream.size -= payloadStartOffset;
  3226. //reassemble PES packet
  3227. pesData = new Uint8Array(stream.size);
  3228. for (var j = 0, dataLen = data.length; j < dataLen; j++) {
  3229. frag = data[j];
  3230. var len = frag.byteLength;
  3231. if (payloadStartOffset) {
  3232. if (payloadStartOffset > len) {
  3233. // trim full frag if PES header bigger than frag
  3234. payloadStartOffset -= len;
  3235. continue;
  3236. } else {
  3237. // trim partial frag if PES header smaller than frag
  3238. frag = frag.subarray(payloadStartOffset);
  3239. len -= payloadStartOffset;
  3240. payloadStartOffset = 0;
  3241. }
  3242. }
  3243. pesData.set(frag, i);
  3244. i += len;
  3245. }
  3246. if (pesLen) {
  3247. // payload size : remove PES header + PES extension
  3248. pesLen -= pesHdrLen + 3;
  3249. }
  3250. return {data: pesData, pts: pesPts, dts: pesDts, len: pesLen};
  3251. } else {
  3252. return null;
  3253. }
  3254. };
  3255. TSDemuxer.prototype.pushAccesUnit = function pushAccesUnit(avcSample, avcTrack) {
  3256. if (avcSample.units.length && avcSample.frame) {
  3257. var samples = avcTrack.samples;
  3258. var nbSamples = samples.length;
  3259. // only push AVC sample if starting with a keyframe is not mandatory OR
  3260. // if keyframe already found in this fragment OR
  3261. // keyframe found in last fragment (track.sps) AND
  3262. // samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous
  3263. if (!this.config.forceKeyFrameOnDiscontinuity || avcSample.key === true || avcTrack.sps && (nbSamples || this.contiguous)) {
  3264. avcSample.id = nbSamples;
  3265. samples.push(avcSample);
  3266. } else {
  3267. // dropped samples, track it
  3268. avcTrack.dropped++;
  3269. }
  3270. }
  3271. if (avcSample.debug.length) {
  3272. logger["b" /* logger */].log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug);
  3273. }
  3274. };
  3275. TSDemuxer.prototype._parseAVCPES = function _parseAVCPES(pes, last) {
  3276. var _this = this;
  3277. //logger.log('parse new PES');
  3278. var track = this._avcTrack,
  3279. units = this._parseAVCNALu(pes.data),
  3280. debug = false,
  3281. expGolombDecoder,
  3282. avcSample = this.avcSample,
  3283. push,
  3284. spsfound = false,
  3285. i,
  3286. pushAccesUnit = this.pushAccesUnit.bind(this),
  3287. createAVCSample = function createAVCSample(key, pts, dts, debug) {
  3288. return {key: key, pts: pts, dts: dts, units: [], debug: debug};
  3289. };
  3290. //free pes.data to save up some memory
  3291. pes.data = null;
  3292. // if new NAL units found and last sample still there, let's push ...
  3293. // this helps parsing streams with missing AUD (only do this if AUD never found)
  3294. if (avcSample && units.length && !track.audFound) {
  3295. pushAccesUnit(avcSample, track);
  3296. avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, '');
  3297. }
  3298. units.forEach(function (unit) {
  3299. switch (unit.type) {
  3300. //NDR
  3301. case 1:
  3302. push = true;
  3303. if (!avcSample) {
  3304. avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');
  3305. }
  3306. if (debug) {
  3307. avcSample.debug += 'NDR ';
  3308. }
  3309. avcSample.frame = true;
  3310. var data = unit.data;
  3311. // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
  3312. if (spsfound && data.length > 4) {
  3313. // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
  3314. var sliceType = new exp_golomb(data).readSliceType();
  3315. // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
  3316. // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
  3317. // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
  3318. // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
  3319. //if (sliceType === 2 || sliceType === 7) {
  3320. if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
  3321. avcSample.key = true;
  3322. }
  3323. }
  3324. break;
  3325. //IDR
  3326. case 5:
  3327. push = true;
  3328. // handle PES not starting with AUD
  3329. if (!avcSample) {
  3330. avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');
  3331. }
  3332. if (debug) {
  3333. avcSample.debug += 'IDR ';
  3334. }
  3335. avcSample.key = true;
  3336. avcSample.frame = true;
  3337. break;
  3338. //SEI
  3339. case 6:
  3340. push = true;
  3341. if (debug && avcSample) {
  3342. avcSample.debug += 'SEI ';
  3343. }
  3344. expGolombDecoder = new exp_golomb(_this.discardEPB(unit.data));
  3345. // skip frameType
  3346. expGolombDecoder.readUByte();
  3347. var payloadType = 0;
  3348. var payloadSize = 0;
  3349. var endOfCaptions = false;
  3350. var b = 0;
  3351. while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) {
  3352. payloadType = 0;
  3353. do {
  3354. b = expGolombDecoder.readUByte();
  3355. payloadType += b;
  3356. } while (b === 0xFF);
  3357. // Parse payload size.
  3358. payloadSize = 0;
  3359. do {
  3360. b = expGolombDecoder.readUByte();
  3361. payloadSize += b;
  3362. } while (b === 0xFF);
  3363. // TODO: there can be more than one payload in an SEI packet...
  3364. // TODO: need to read type and size in a while loop to get them all
  3365. if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) {
  3366. endOfCaptions = true;
  3367. var countryCode = expGolombDecoder.readUByte();
  3368. if (countryCode === 181) {
  3369. var providerCode = expGolombDecoder.readUShort();
  3370. if (providerCode === 49) {
  3371. var userStructure = expGolombDecoder.readUInt();
  3372. if (userStructure === 0x47413934) {
  3373. var userDataType = expGolombDecoder.readUByte();
  3374. // Raw CEA-608 bytes wrapped in CEA-708 packet
  3375. if (userDataType === 3) {
  3376. var firstByte = expGolombDecoder.readUByte();
  3377. var secondByte = expGolombDecoder.readUByte();
  3378. var totalCCs = 31 & firstByte;
  3379. var byteArray = [firstByte, secondByte];
  3380. for (i = 0; i < totalCCs; i++) {
  3381. // 3 bytes per CC
  3382. byteArray.push(expGolombDecoder.readUByte());
  3383. byteArray.push(expGolombDecoder.readUByte());
  3384. byteArray.push(expGolombDecoder.readUByte());
  3385. }
  3386. _this._insertSampleInOrder(_this._txtTrack.samples, {
  3387. type: 3,
  3388. pts: pes.pts,
  3389. bytes: byteArray
  3390. });
  3391. }
  3392. }
  3393. }
  3394. }
  3395. } else if (payloadSize < expGolombDecoder.bytesAvailable) {
  3396. for (i = 0; i < payloadSize; i++) {
  3397. expGolombDecoder.readUByte();
  3398. }
  3399. }
  3400. }
  3401. break;
  3402. //SPS
  3403. case 7:
  3404. push = true;
  3405. spsfound = true;
  3406. if (debug && avcSample) {
  3407. avcSample.debug += 'SPS ';
  3408. }
  3409. if (!track.sps) {
  3410. expGolombDecoder = new exp_golomb(unit.data);
  3411. var config = expGolombDecoder.readSPS();
  3412. track.width = config.width;
  3413. track.height = config.height;
  3414. track.pixelRatio = config.pixelRatio;
  3415. track.sps = [unit.data];
  3416. track.duration = _this._duration;
  3417. var codecarray = unit.data.subarray(1, 4);
  3418. var codecstring = 'avc1.';
  3419. for (i = 0; i < 3; i++) {
  3420. var h = codecarray[i].toString(16);
  3421. if (h.length < 2) {
  3422. h = '0' + h;
  3423. }
  3424. codecstring += h;
  3425. }
  3426. track.codec = codecstring;
  3427. }
  3428. break;
  3429. //PPS
  3430. case 8:
  3431. push = true;
  3432. if (debug && avcSample) {
  3433. avcSample.debug += 'PPS ';
  3434. }
  3435. if (!track.pps) {
  3436. track.pps = [unit.data];
  3437. }
  3438. break;
  3439. // AUD
  3440. case 9:
  3441. push = false;
  3442. track.audFound = true;
  3443. if (avcSample) {
  3444. pushAccesUnit(avcSample, track);
  3445. }
  3446. avcSample = _this.avcSample = createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : '');
  3447. break;
  3448. // Filler Data
  3449. case 12:
  3450. push = false;
  3451. break;
  3452. default:
  3453. push = false;
  3454. if (avcSample) {
  3455. avcSample.debug += 'unknown NAL ' + unit.type + ' ';
  3456. }
  3457. break;
  3458. }
  3459. if (avcSample && push) {
  3460. var _units = avcSample.units;
  3461. _units.push(unit);
  3462. }
  3463. });
  3464. // if last PES packet, push samples
  3465. if (last && avcSample) {
  3466. pushAccesUnit(avcSample, track);
  3467. this.avcSample = null;
  3468. }
  3469. };
  3470. TSDemuxer.prototype._insertSampleInOrder = function _insertSampleInOrder(arr, data) {
  3471. var len = arr.length;
  3472. if (len > 0) {
  3473. if (data.pts >= arr[len - 1].pts) {
  3474. arr.push(data);
  3475. } else {
  3476. for (var pos = len - 1; pos >= 0; pos--) {
  3477. if (data.pts < arr[pos].pts) {
  3478. arr.splice(pos, 0, data);
  3479. break;
  3480. }
  3481. }
  3482. }
  3483. } else {
  3484. arr.push(data);
  3485. }
  3486. };
  3487. TSDemuxer.prototype._getLastNalUnit = function _getLastNalUnit() {
  3488. var avcSample = this.avcSample,
  3489. lastUnit = void 0;
  3490. // try to fallback to previous sample if current one is empty
  3491. if (!avcSample || avcSample.units.length === 0) {
  3492. var track = this._avcTrack,
  3493. samples = track.samples;
  3494. avcSample = samples[samples.length - 1];
  3495. }
  3496. if (avcSample) {
  3497. var units = avcSample.units;
  3498. lastUnit = units[units.length - 1];
  3499. }
  3500. return lastUnit;
  3501. };
  3502. TSDemuxer.prototype._parseAVCNALu = function _parseAVCNALu(array) {
  3503. var i = 0,
  3504. len = array.byteLength,
  3505. value,
  3506. overflow,
  3507. track = this._avcTrack,
  3508. state = track.naluState || 0,
  3509. lastState = state;
  3510. var units = [],
  3511. unit,
  3512. unitType,
  3513. lastUnitStart = -1,
  3514. lastUnitType;
  3515. //logger.log('PES:' + Hex.hexDump(array));
  3516. if (state === -1) {
  3517. // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
  3518. lastUnitStart = 0;
  3519. // NALu type is value read from offset 0
  3520. lastUnitType = array[0] & 0x1f;
  3521. state = 0;
  3522. i = 1;
  3523. }
  3524. while (i < len) {
  3525. value = array[i++];
  3526. // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
  3527. if (!state) {
  3528. state = value ? 0 : 1;
  3529. continue;
  3530. }
  3531. if (state === 1) {
  3532. state = value ? 0 : 2;
  3533. continue;
  3534. }
  3535. // here we have state either equal to 2 or 3
  3536. if (!value) {
  3537. state = 3;
  3538. } else if (value === 1) {
  3539. if (lastUnitStart >= 0) {
  3540. unit = {data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType};
  3541. //logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
  3542. units.push(unit);
  3543. } else {
  3544. // lastUnitStart is undefined => this is the first start code found in this PES packet
  3545. // first check if start code delimiter is overlapping between 2 PES packets,
  3546. // ie it started in last packet (lastState not zero)
  3547. // and ended at the beginning of this PES packet (i <= 4 - lastState)
  3548. var lastUnit = this._getLastNalUnit();
  3549. if (lastUnit) {
  3550. if (lastState && i <= 4 - lastState) {
  3551. // start delimiter overlapping between PES packets
  3552. // strip start delimiter bytes from the end of last NAL unit
  3553. // check if lastUnit had a state different from zero
  3554. if (lastUnit.state) {
  3555. // strip last bytes
  3556. lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
  3557. }
  3558. }
  3559. // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
  3560. overflow = i - state - 1;
  3561. if (overflow > 0) {
  3562. //logger.log('first NALU found with overflow:' + overflow);
  3563. var tmp = new Uint8Array(lastUnit.data.byteLength + overflow);
  3564. tmp.set(lastUnit.data, 0);
  3565. tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength);
  3566. lastUnit.data = tmp;
  3567. }
  3568. }
  3569. }
  3570. // check if we can read unit type
  3571. if (i < len) {
  3572. unitType = array[i] & 0x1f;
  3573. //logger.log('find NALU @ offset:' + i + ',type:' + unitType);
  3574. lastUnitStart = i;
  3575. lastUnitType = unitType;
  3576. state = 0;
  3577. } else {
  3578. // not enough byte to read unit type. let's read it on next PES parsing
  3579. state = -1;
  3580. }
  3581. } else {
  3582. state = 0;
  3583. }
  3584. }
  3585. if (lastUnitStart >= 0 && state >= 0) {
  3586. unit = {data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state};
  3587. units.push(unit);
  3588. //logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
  3589. }
  3590. // no NALu found
  3591. if (units.length === 0) {
  3592. // append pes.data to previous NAL unit
  3593. var _lastUnit = this._getLastNalUnit();
  3594. if (_lastUnit) {
  3595. var _tmp = new Uint8Array(_lastUnit.data.byteLength + array.byteLength);
  3596. _tmp.set(_lastUnit.data, 0);
  3597. _tmp.set(array, _lastUnit.data.byteLength);
  3598. _lastUnit.data = _tmp;
  3599. }
  3600. }
  3601. track.naluState = state;
  3602. return units;
  3603. };
  3604. /**
  3605. * remove Emulation Prevention bytes from a RBSP
  3606. */
  3607. TSDemuxer.prototype.discardEPB = function discardEPB(data) {
  3608. var length = data.byteLength,
  3609. EPBPositions = [],
  3610. i = 1,
  3611. newLength,
  3612. newData;
  3613. // Find all `Emulation Prevention Bytes`
  3614. while (i < length - 2) {
  3615. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  3616. EPBPositions.push(i + 2);
  3617. i += 2;
  3618. } else {
  3619. i++;
  3620. }
  3621. }
  3622. // If no Emulation Prevention Bytes were found just return the original
  3623. // array
  3624. if (EPBPositions.length === 0) {
  3625. return data;
  3626. }
  3627. // Create a new array to hold the NAL unit data
  3628. newLength = length - EPBPositions.length;
  3629. newData = new Uint8Array(newLength);
  3630. var sourceIndex = 0;
  3631. for (i = 0; i < newLength; sourceIndex++, i++) {
  3632. if (sourceIndex === EPBPositions[0]) {
  3633. // Skip this byte
  3634. sourceIndex++;
  3635. // Remove this position index
  3636. EPBPositions.shift();
  3637. }
  3638. newData[i] = data[sourceIndex];
  3639. }
  3640. return newData;
  3641. };
  3642. TSDemuxer.prototype._parseAACPES = function _parseAACPES(pes) {
  3643. var track = this._audioTrack,
  3644. data = pes.data,
  3645. pts = pes.pts,
  3646. startOffset = 0,
  3647. aacOverFlow = this.aacOverFlow,
  3648. aacLastPTS = this.aacLastPTS,
  3649. frameDuration,
  3650. frameIndex,
  3651. offset,
  3652. stamp,
  3653. len;
  3654. if (aacOverFlow) {
  3655. var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
  3656. tmp.set(aacOverFlow, 0);
  3657. tmp.set(data, aacOverFlow.byteLength);
  3658. //logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`);
  3659. data = tmp;
  3660. }
  3661. // look for ADTS header (0xFFFx)
  3662. for (offset = startOffset, len = data.length; offset < len - 1; offset++) {
  3663. if (isHeader(data, offset)) {
  3664. break;
  3665. }
  3666. }
  3667. // if ADTS header does not start straight from the beginning of the PES payload, raise an error
  3668. if (offset) {
  3669. var reason, fatal;
  3670. if (offset < len - 1) {
  3671. reason = 'AAC PES did not start with ADTS header,offset:' + offset;
  3672. fatal = false;
  3673. } else {
  3674. reason = 'no ADTS header found in AAC PES';
  3675. fatal = true;
  3676. }
  3677. logger["b" /* logger */].warn('parsing error:' + reason);
  3678. this.observer.trigger(events["a" /* default */].ERROR, {
  3679. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  3680. details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR,
  3681. fatal: fatal,
  3682. reason: reason
  3683. });
  3684. if (fatal) {
  3685. return;
  3686. }
  3687. }
  3688. initTrackConfig(track, this.observer, data, offset, this.audioCodec);
  3689. frameIndex = 0;
  3690. frameDuration = getFrameDuration(track.samplerate);
  3691. // if last AAC frame is overflowing, we should ensure timestamps are contiguous:
  3692. // first sample PTS should be equal to last sample PTS + frameDuration
  3693. if (aacOverFlow && aacLastPTS) {
  3694. var newPTS = aacLastPTS + frameDuration;
  3695. if (Math.abs(newPTS - pts) > 1) {
  3696. logger["b" /* logger */].log('AAC: align PTS for overlapping frames by ' + Math.round((newPTS - pts) / 90));
  3697. pts = newPTS;
  3698. }
  3699. }
  3700. //scan for aac samples
  3701. while (offset < len) {
  3702. if (isHeader(data, offset) && offset + 5 < len) {
  3703. var frame = appendFrame(track, data, offset, pts, frameIndex);
  3704. if (frame) {
  3705. //logger.log(`${Math.round(frame.sample.pts)} : AAC`);
  3706. offset += frame.length;
  3707. stamp = frame.sample.pts;
  3708. frameIndex++;
  3709. } else {
  3710. //logger.log('Unable to parse AAC frame');
  3711. break;
  3712. }
  3713. } else {
  3714. //nothing found, keep looking
  3715. offset++;
  3716. }
  3717. }
  3718. if (offset < len) {
  3719. aacOverFlow = data.subarray(offset, len);
  3720. //logger.log(`AAC: overflow detected:${len-offset}`);
  3721. } else {
  3722. aacOverFlow = null;
  3723. }
  3724. this.aacOverFlow = aacOverFlow;
  3725. this.aacLastPTS = stamp;
  3726. };
  3727. TSDemuxer.prototype._parseMPEGPES = function _parseMPEGPES(pes) {
  3728. var data = pes.data;
  3729. var length = data.length;
  3730. var frameIndex = 0;
  3731. var offset = 0;
  3732. var pts = pes.pts;
  3733. while (offset < length) {
  3734. if (mpegaudio.isHeader(data, offset)) {
  3735. var frame = mpegaudio.appendFrame(this._audioTrack, data, offset, pts, frameIndex);
  3736. if (frame) {
  3737. offset += frame.length;
  3738. frameIndex++;
  3739. } else {
  3740. //logger.log('Unable to parse Mpeg audio frame');
  3741. break;
  3742. }
  3743. } else {
  3744. //nothing found, keep looking
  3745. offset++;
  3746. }
  3747. }
  3748. };
  3749. TSDemuxer.prototype._parseID3PES = function _parseID3PES(pes) {
  3750. this._id3Track.samples.push(pes);
  3751. };
  3752. return TSDemuxer;
  3753. }();
  3754. /* harmony default export */
  3755. var tsdemuxer = (tsdemuxer_TSDemuxer);
  3756. // CONCATENATED MODULE: ./src/demux/mp3demuxer.js
  3757. function mp3demuxer__classCallCheck(instance, Constructor) {
  3758. if (!(instance instanceof Constructor)) {
  3759. throw new TypeError("Cannot call a class as a function");
  3760. }
  3761. }
  3762. /**
  3763. * MP3 demuxer
  3764. */
  3765. var mp3demuxer_MP3Demuxer = function () {
  3766. function MP3Demuxer(observer, remuxer, config) {
  3767. mp3demuxer__classCallCheck(this, MP3Demuxer);
  3768. this.observer = observer;
  3769. this.config = config;
  3770. this.remuxer = remuxer;
  3771. }
  3772. MP3Demuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
  3773. this._audioTrack = {
  3774. container: 'audio/mpeg',
  3775. type: 'audio',
  3776. id: -1,
  3777. sequenceNumber: 0,
  3778. isAAC: false,
  3779. samples: [],
  3780. len: 0,
  3781. manifestCodec: audioCodec,
  3782. duration: duration,
  3783. inputTimeScale: 90000
  3784. };
  3785. };
  3786. MP3Demuxer.prototype.resetTimeStamp = function resetTimeStamp() {
  3787. };
  3788. MP3Demuxer.probe = function probe(data) {
  3789. // check if data contains ID3 timestamp and MPEG sync word
  3790. var offset, length;
  3791. var id3Data = id3["a" /* default */].getID3Data(data, 0);
  3792. if (id3Data && id3["a" /* default */].getTimeStamp(id3Data) !== undefined) {
  3793. // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
  3794. // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
  3795. // More info http://www.mp3-tech.org/programmer/frame_header.html
  3796. for (offset = id3Data.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) {
  3797. if (mpegaudio.probe(data, offset)) {
  3798. logger["b" /* logger */].log('MPEG Audio sync word found !');
  3799. return true;
  3800. }
  3801. }
  3802. }
  3803. return false;
  3804. };
  3805. // feed incoming data to the front of the parsing pipeline
  3806. MP3Demuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
  3807. var id3Data = id3["a" /* default */].getID3Data(data, 0);
  3808. var timestamp = id3["a" /* default */].getTimeStamp(id3Data);
  3809. var pts = timestamp ? 90 * timestamp : timeOffset * 90000;
  3810. var offset = id3Data.length;
  3811. var length = data.length;
  3812. var frameIndex = 0,
  3813. stamp = 0;
  3814. var track = this._audioTrack;
  3815. var id3Samples = [{pts: pts, dts: pts, data: id3Data}];
  3816. while (offset < length) {
  3817. if (mpegaudio.isHeader(data, offset)) {
  3818. var frame = mpegaudio.appendFrame(track, data, offset, pts, frameIndex);
  3819. if (frame) {
  3820. offset += frame.length;
  3821. stamp = frame.sample.pts;
  3822. frameIndex++;
  3823. } else {
  3824. //logger.log('Unable to parse Mpeg audio frame');
  3825. break;
  3826. }
  3827. } else if (id3["a" /* default */].isHeader(data, offset)) {
  3828. id3Data = id3["a" /* default */].getID3Data(data, offset);
  3829. id3Samples.push({pts: stamp, dts: stamp, data: id3Data});
  3830. offset += id3Data.length;
  3831. } else {
  3832. //nothing found, keep looking
  3833. offset++;
  3834. }
  3835. }
  3836. this.remuxer.remux(track, {samples: []}, {
  3837. samples: id3Samples,
  3838. inputTimeScale: 90000
  3839. }, {samples: []}, timeOffset, contiguous, accurateTimeOffset);
  3840. };
  3841. MP3Demuxer.prototype.destroy = function destroy() {
  3842. };
  3843. return MP3Demuxer;
  3844. }();
  3845. /* harmony default export */
  3846. var mp3demuxer = (mp3demuxer_MP3Demuxer);
  3847. // CONCATENATED MODULE: ./src/helper/aac.js
  3848. function aac__classCallCheck(instance, Constructor) {
  3849. if (!(instance instanceof Constructor)) {
  3850. throw new TypeError("Cannot call a class as a function");
  3851. }
  3852. }
  3853. /**
  3854. * AAC helper
  3855. */
  3856. var AAC = function () {
  3857. function AAC() {
  3858. aac__classCallCheck(this, AAC);
  3859. }
  3860. AAC.getSilentFrame = function getSilentFrame(codec, channelCount) {
  3861. switch (codec) {
  3862. case 'mp4a.40.2':
  3863. if (channelCount === 1) {
  3864. return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);
  3865. } else if (channelCount === 2) {
  3866. return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);
  3867. } else if (channelCount === 3) {
  3868. return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);
  3869. } else if (channelCount === 4) {
  3870. return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);
  3871. } else if (channelCount === 5) {
  3872. return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);
  3873. } else if (channelCount === 6) {
  3874. return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]);
  3875. }
  3876. break;
  3877. // handle HE-AAC below (mp4a.40.5 / mp4a.40.29)
  3878. default:
  3879. if (channelCount === 1) {
  3880. // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
  3881. return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
  3882. } else if (channelCount === 2) {
  3883. // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
  3884. return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
  3885. } else if (channelCount === 3) {
  3886. // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
  3887. return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
  3888. }
  3889. break;
  3890. }
  3891. return null;
  3892. };
  3893. return AAC;
  3894. }();
  3895. /* harmony default export */
  3896. var aac = (AAC);
  3897. // CONCATENATED MODULE: ./src/remux/mp4-generator.js
  3898. function mp4_generator__classCallCheck(instance, Constructor) {
  3899. if (!(instance instanceof Constructor)) {
  3900. throw new TypeError("Cannot call a class as a function");
  3901. }
  3902. }
  3903. /**
  3904. * Generate MP4 Box
  3905. */
  3906. //import Hex from '../utils/hex';
  3907. var UINT32_MAX = Math.pow(2, 32) - 1;
  3908. var MP4 = function () {
  3909. function MP4() {
  3910. mp4_generator__classCallCheck(this, MP4);
  3911. }
  3912. MP4.init = function init() {
  3913. MP4.types = {
  3914. avc1: [], // codingname
  3915. avcC: [],
  3916. btrt: [],
  3917. dinf: [],
  3918. dref: [],
  3919. esds: [],
  3920. ftyp: [],
  3921. hdlr: [],
  3922. mdat: [],
  3923. mdhd: [],
  3924. mdia: [],
  3925. mfhd: [],
  3926. minf: [],
  3927. moof: [],
  3928. moov: [],
  3929. mp4a: [],
  3930. '.mp3': [],
  3931. mvex: [],
  3932. mvhd: [],
  3933. pasp: [],
  3934. sdtp: [],
  3935. stbl: [],
  3936. stco: [],
  3937. stsc: [],
  3938. stsd: [],
  3939. stsz: [],
  3940. stts: [],
  3941. tfdt: [],
  3942. tfhd: [],
  3943. traf: [],
  3944. trak: [],
  3945. trun: [],
  3946. trex: [],
  3947. tkhd: [],
  3948. vmhd: [],
  3949. smhd: []
  3950. };
  3951. var i;
  3952. for (i in MP4.types) {
  3953. if (MP4.types.hasOwnProperty(i)) {
  3954. MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
  3955. }
  3956. }
  3957. var videoHdlr = new Uint8Array([0x00, // version 0
  3958. 0x00, 0x00, 0x00, // flags
  3959. 0x00, 0x00, 0x00, 0x00, // pre_defined
  3960. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  3961. 0x00, 0x00, 0x00, 0x00, // reserved
  3962. 0x00, 0x00, 0x00, 0x00, // reserved
  3963. 0x00, 0x00, 0x00, 0x00, // reserved
  3964. 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  3965. ]);
  3966. var audioHdlr = new Uint8Array([0x00, // version 0
  3967. 0x00, 0x00, 0x00, // flags
  3968. 0x00, 0x00, 0x00, 0x00, // pre_defined
  3969. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  3970. 0x00, 0x00, 0x00, 0x00, // reserved
  3971. 0x00, 0x00, 0x00, 0x00, // reserved
  3972. 0x00, 0x00, 0x00, 0x00, // reserved
  3973. 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  3974. ]);
  3975. MP4.HDLR_TYPES = {
  3976. 'video': videoHdlr,
  3977. 'audio': audioHdlr
  3978. };
  3979. var dref = new Uint8Array([0x00, // version 0
  3980. 0x00, 0x00, 0x00, // flags
  3981. 0x00, 0x00, 0x00, 0x01, // entry_count
  3982. 0x00, 0x00, 0x00, 0x0c, // entry_size
  3983. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  3984. 0x00, // version 0
  3985. 0x00, 0x00, 0x01 // entry_flags
  3986. ]);
  3987. var stco = new Uint8Array([0x00, // version
  3988. 0x00, 0x00, 0x00, // flags
  3989. 0x00, 0x00, 0x00, 0x00 // entry_count
  3990. ]);
  3991. MP4.STTS = MP4.STSC = MP4.STCO = stco;
  3992. MP4.STSZ = new Uint8Array([0x00, // version
  3993. 0x00, 0x00, 0x00, // flags
  3994. 0x00, 0x00, 0x00, 0x00, // sample_size
  3995. 0x00, 0x00, 0x00, 0x00]);
  3996. MP4.VMHD = new Uint8Array([0x00, // version
  3997. 0x00, 0x00, 0x01, // flags
  3998. 0x00, 0x00, // graphicsmode
  3999. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
  4000. ]);
  4001. MP4.SMHD = new Uint8Array([0x00, // version
  4002. 0x00, 0x00, 0x00, // flags
  4003. 0x00, 0x00, // balance
  4004. 0x00, 0x00 // reserved
  4005. ]);
  4006. MP4.STSD = new Uint8Array([0x00, // version 0
  4007. 0x00, 0x00, 0x00, // flags
  4008. 0x00, 0x00, 0x00, 0x01]); // entry_count
  4009. var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
  4010. var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
  4011. var minorVersion = new Uint8Array([0, 0, 0, 1]);
  4012. MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);
  4013. MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
  4014. };
  4015. MP4.box = function box(type) {
  4016. var payload = Array.prototype.slice.call(arguments, 1),
  4017. size = 8,
  4018. i = payload.length,
  4019. len = i,
  4020. result;
  4021. // calculate the total size we need to allocate
  4022. while (i--) {
  4023. size += payload[i].byteLength;
  4024. }
  4025. result = new Uint8Array(size);
  4026. result[0] = size >> 24 & 0xff;
  4027. result[1] = size >> 16 & 0xff;
  4028. result[2] = size >> 8 & 0xff;
  4029. result[3] = size & 0xff;
  4030. result.set(type, 4);
  4031. // copy the payload into the result
  4032. for (i = 0, size = 8; i < len; i++) {
  4033. // copy payload[i] array @ offset size
  4034. result.set(payload[i], size);
  4035. size += payload[i].byteLength;
  4036. }
  4037. return result;
  4038. };
  4039. MP4.hdlr = function hdlr(type) {
  4040. return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);
  4041. };
  4042. MP4.mdat = function mdat(data) {
  4043. return MP4.box(MP4.types.mdat, data);
  4044. };
  4045. MP4.mdhd = function mdhd(timescale, duration) {
  4046. duration *= timescale;
  4047. var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
  4048. var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
  4049. return MP4.box(MP4.types.mdhd, new Uint8Array([0x01, // version 1
  4050. 0x00, 0x00, 0x00, // flags
  4051. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
  4052. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
  4053. timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
  4054. upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x55, 0xc4, // 'und' language (undetermined)
  4055. 0x00, 0x00]));
  4056. };
  4057. MP4.mdia = function mdia(track) {
  4058. return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track));
  4059. };
  4060. MP4.mfhd = function mfhd(sequenceNumber) {
  4061. return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
  4062. sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF]) // sequence_number
  4063. );
  4064. };
  4065. MP4.minf = function minf(track) {
  4066. if (track.type === 'audio') {
  4067. return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track));
  4068. } else {
  4069. return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track));
  4070. }
  4071. };
  4072. MP4.moof = function moof(sn, baseMediaDecodeTime, track) {
  4073. return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime));
  4074. };
  4075. /**
  4076. * @param tracks... (optional) {array} the tracks associated with this movie
  4077. */
  4078. MP4.moov = function moov(tracks) {
  4079. var i = tracks.length,
  4080. boxes = [];
  4081. while (i--) {
  4082. boxes[i] = MP4.trak(tracks[i]);
  4083. }
  4084. return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks)));
  4085. };
  4086. MP4.mvex = function mvex(tracks) {
  4087. var i = tracks.length,
  4088. boxes = [];
  4089. while (i--) {
  4090. boxes[i] = MP4.trex(tracks[i]);
  4091. }
  4092. return MP4.box.apply(null, [MP4.types.mvex].concat(boxes));
  4093. };
  4094. MP4.mvhd = function mvhd(timescale, duration) {
  4095. duration *= timescale;
  4096. var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
  4097. var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
  4098. var bytes = new Uint8Array([0x01, // version 1
  4099. 0x00, 0x00, 0x00, // flags
  4100. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
  4101. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
  4102. timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
  4103. upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  4104. 0x01, 0x00, // 1.0 volume
  4105. 0x00, 0x00, // reserved
  4106. 0x00, 0x00, 0x00, 0x00, // reserved
  4107. 0x00, 0x00, 0x00, 0x00, // reserved
  4108. 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  4109. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
  4110. 0xff, 0xff, 0xff, 0xff // next_track_ID
  4111. ]);
  4112. return MP4.box(MP4.types.mvhd, bytes);
  4113. };
  4114. MP4.sdtp = function sdtp(track) {
  4115. var samples = track.samples || [],
  4116. bytes = new Uint8Array(4 + samples.length),
  4117. flags,
  4118. i;
  4119. // leave the full box header (4 bytes) all zero
  4120. // write the sample table
  4121. for (i = 0; i < samples.length; i++) {
  4122. flags = samples[i].flags;
  4123. bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
  4124. }
  4125. return MP4.box(MP4.types.sdtp, bytes);
  4126. };
  4127. MP4.stbl = function stbl(track) {
  4128. return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO));
  4129. };
  4130. MP4.avc1 = function avc1(track) {
  4131. var sps = [],
  4132. pps = [],
  4133. i,
  4134. data,
  4135. len;
  4136. // assemble the SPSs
  4137. for (i = 0; i < track.sps.length; i++) {
  4138. data = track.sps[i];
  4139. len = data.byteLength;
  4140. sps.push(len >>> 8 & 0xFF);
  4141. sps.push(len & 0xFF);
  4142. sps = sps.concat(Array.prototype.slice.call(data)); // SPS
  4143. }
  4144. // assemble the PPSs
  4145. for (i = 0; i < track.pps.length; i++) {
  4146. data = track.pps[i];
  4147. len = data.byteLength;
  4148. pps.push(len >>> 8 & 0xFF);
  4149. pps.push(len & 0xFF);
  4150. pps = pps.concat(Array.prototype.slice.call(data));
  4151. }
  4152. var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version
  4153. sps[3], // profile
  4154. sps[4], // profile compat
  4155. sps[5], // level
  4156. 0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes
  4157. 0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
  4158. ].concat(sps).concat([track.pps.length // numOfPictureParameterSets
  4159. ]).concat(pps))),
  4160. // "PPS"
  4161. width = track.width,
  4162. height = track.height,
  4163. hSpacing = track.pixelRatio[0],
  4164. vSpacing = track.pixelRatio[1];
  4165. return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved
  4166. 0x00, 0x00, 0x00, // reserved
  4167. 0x00, 0x01, // data_reference_index
  4168. 0x00, 0x00, // pre_defined
  4169. 0x00, 0x00, // reserved
  4170. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
  4171. width >> 8 & 0xFF, width & 0xff, // width
  4172. height >> 8 & 0xFF, height & 0xff, // height
  4173. 0x00, 0x48, 0x00, 0x00, // horizresolution
  4174. 0x00, 0x48, 0x00, 0x00, // vertresolution
  4175. 0x00, 0x00, 0x00, 0x00, // reserved
  4176. 0x00, 0x01, // frame_count
  4177. 0x12, 0x64, 0x61, 0x69, 0x6C, //dailymotion/hls.js
  4178. 0x79, 0x6D, 0x6F, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x68, 0x6C, 0x73, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname
  4179. 0x00, 0x18, // depth = 24
  4180. 0x11, 0x11]), // pre_defined = -1
  4181. avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  4182. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  4183. 0x00, 0x2d, 0xc6, 0xc0])), // avgBitrate
  4184. MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, // hSpacing
  4185. hSpacing >> 16 & 0xFF, hSpacing >> 8 & 0xFF, hSpacing & 0xFF, vSpacing >> 24, // vSpacing
  4186. vSpacing >> 16 & 0xFF, vSpacing >> 8 & 0xFF, vSpacing & 0xFF])));
  4187. };
  4188. MP4.esds = function esds(track) {
  4189. var configlen = track.config.length;
  4190. return new Uint8Array([0x00, // version 0
  4191. 0x00, 0x00, 0x00, // flags
  4192. 0x03, // descriptor_type
  4193. 0x17 + configlen, // length
  4194. 0x00, 0x01, //es_id
  4195. 0x00, // stream_priority
  4196. 0x04, // descriptor_type
  4197. 0x0f + configlen, // length
  4198. 0x40, //codec : mpeg4_audio
  4199. 0x15, // stream_type
  4200. 0x00, 0x00, 0x00, // buffer_size
  4201. 0x00, 0x00, 0x00, 0x00, // maxBitrate
  4202. 0x00, 0x00, 0x00, 0x00, // avgBitrate
  4203. 0x05 // descriptor_type
  4204. ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
  4205. };
  4206. MP4.mp4a = function mp4a(track) {
  4207. var samplerate = track.samplerate;
  4208. return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved
  4209. 0x00, 0x00, 0x00, // reserved
  4210. 0x00, 0x01, // data_reference_index
  4211. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
  4212. 0x00, track.channelCount, // channelcount
  4213. 0x00, 0x10, // sampleSize:16bits
  4214. 0x00, 0x00, 0x00, 0x00, // reserved2
  4215. samplerate >> 8 & 0xFF, samplerate & 0xff, //
  4216. 0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));
  4217. };
  4218. MP4.mp3 = function mp3(track) {
  4219. var samplerate = track.samplerate;
  4220. return MP4.box(MP4.types['.mp3'], new Uint8Array([0x00, 0x00, 0x00, // reserved
  4221. 0x00, 0x00, 0x00, // reserved
  4222. 0x00, 0x01, // data_reference_index
  4223. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
  4224. 0x00, track.channelCount, // channelcount
  4225. 0x00, 0x10, // sampleSize:16bits
  4226. 0x00, 0x00, 0x00, 0x00, // reserved2
  4227. samplerate >> 8 & 0xFF, samplerate & 0xff, //
  4228. 0x00, 0x00]));
  4229. };
  4230. MP4.stsd = function stsd(track) {
  4231. if (track.type === 'audio') {
  4232. if (!track.isAAC && track.codec === 'mp3') {
  4233. return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track));
  4234. }
  4235. return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
  4236. } else {
  4237. return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
  4238. }
  4239. };
  4240. MP4.tkhd = function tkhd(track) {
  4241. var id = track.id,
  4242. duration = track.duration * track.timescale,
  4243. width = track.width,
  4244. height = track.height,
  4245. upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)),
  4246. lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
  4247. return MP4.box(MP4.types.tkhd, new Uint8Array([0x01, // version 1
  4248. 0x00, 0x00, 0x07, // flags
  4249. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
  4250. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
  4251. id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID
  4252. 0x00, 0x00, 0x00, 0x00, // reserved
  4253. upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
  4254. 0x00, 0x00, // layer
  4255. 0x00, 0x00, // alternate_group
  4256. 0x00, 0x00, // non-audio track volume
  4257. 0x00, 0x00, // reserved
  4258. 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  4259. width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width
  4260. height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height
  4261. ]));
  4262. };
  4263. MP4.traf = function traf(track, baseMediaDecodeTime) {
  4264. var sampleDependencyTable = MP4.sdtp(track),
  4265. id = track.id,
  4266. upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)),
  4267. lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
  4268. return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0
  4269. 0x00, 0x00, 0x00, // flags
  4270. id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF]) // track_ID
  4271. ), MP4.box(MP4.types.tfdt, new Uint8Array([0x01, // version 1
  4272. 0x00, 0x00, 0x00, // flags
  4273. upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0XFF, upperWordBaseMediaDecodeTime >> 8 & 0XFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0XFF, lowerWordBaseMediaDecodeTime >> 8 & 0XFF, lowerWordBaseMediaDecodeTime & 0xFF])), MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd
  4274. 20 + // tfdt
  4275. 8 + // traf header
  4276. 16 + // mfhd
  4277. 8 + // moof header
  4278. 8), // mdat header
  4279. sampleDependencyTable);
  4280. };
  4281. /**
  4282. * Generate a track box.
  4283. * @param track {object} a track definition
  4284. * @return {Uint8Array} the track box
  4285. */
  4286. MP4.trak = function trak(track) {
  4287. track.duration = track.duration || 0xffffffff;
  4288. return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));
  4289. };
  4290. MP4.trex = function trex(track) {
  4291. var id = track.id;
  4292. return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0
  4293. 0x00, 0x00, 0x00, // flags
  4294. id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID
  4295. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  4296. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  4297. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  4298. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  4299. ]));
  4300. };
  4301. MP4.trun = function trun(track, offset) {
  4302. var samples = track.samples || [],
  4303. len = samples.length,
  4304. arraylen = 12 + 16 * len,
  4305. array = new Uint8Array(arraylen),
  4306. i,
  4307. sample,
  4308. duration,
  4309. size,
  4310. flags,
  4311. cts;
  4312. offset += 8 + arraylen;
  4313. array.set([0x00, // version 0
  4314. 0x00, 0x0f, 0x01, // flags
  4315. len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count
  4316. offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset
  4317. ], 0);
  4318. for (i = 0; i < len; i++) {
  4319. sample = samples[i];
  4320. duration = sample.duration;
  4321. size = sample.size;
  4322. flags = sample.flags;
  4323. cts = sample.cts;
  4324. array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration
  4325. size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size
  4326. flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags
  4327. cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset
  4328. ], 12 + 16 * i);
  4329. }
  4330. return MP4.box(MP4.types.trun, array);
  4331. };
  4332. MP4.initSegment = function initSegment(tracks) {
  4333. if (!MP4.types) {
  4334. MP4.init();
  4335. }
  4336. var movie = MP4.moov(tracks),
  4337. result;
  4338. result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength);
  4339. result.set(MP4.FTYP);
  4340. result.set(movie, MP4.FTYP.byteLength);
  4341. return result;
  4342. };
  4343. return MP4;
  4344. }();
  4345. /* harmony default export */
  4346. var mp4_generator = (MP4);
  4347. // CONCATENATED MODULE: ./src/remux/mp4-remuxer.js
  4348. function mp4_remuxer__classCallCheck(instance, Constructor) {
  4349. if (!(instance instanceof Constructor)) {
  4350. throw new TypeError("Cannot call a class as a function");
  4351. }
  4352. }
  4353. /**
  4354. * fMP4 remuxer
  4355. */
  4356. // 10 seconds
  4357. var MAX_SILENT_FRAME_DURATION = 10 * 1000;
  4358. var mp4_remuxer_MP4Remuxer = function () {
  4359. function MP4Remuxer(observer, config, typeSupported, vendor) {
  4360. mp4_remuxer__classCallCheck(this, MP4Remuxer);
  4361. this.observer = observer;
  4362. this.config = config;
  4363. this.typeSupported = typeSupported;
  4364. var userAgent = navigator.userAgent;
  4365. this.isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS');
  4366. this.ISGenerated = false;
  4367. }
  4368. MP4Remuxer.prototype.destroy = function destroy() {
  4369. };
  4370. MP4Remuxer.prototype.resetTimeStamp = function resetTimeStamp(defaultTimeStamp) {
  4371. this._initPTS = this._initDTS = defaultTimeStamp;
  4372. };
  4373. MP4Remuxer.prototype.resetInitSegment = function resetInitSegment() {
  4374. this.ISGenerated = false;
  4375. };
  4376. MP4Remuxer.prototype.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
  4377. // generate Init Segment if needed
  4378. if (!this.ISGenerated) {
  4379. this.generateIS(audioTrack, videoTrack, timeOffset);
  4380. }
  4381. if (this.ISGenerated) {
  4382. var nbAudioSamples = audioTrack.samples.length;
  4383. var nbVideoSamples = videoTrack.samples.length;
  4384. var audioTimeOffset = timeOffset;
  4385. var videoTimeOffset = timeOffset;
  4386. if (nbAudioSamples && nbVideoSamples) {
  4387. // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS)
  4388. // if first audio DTS is not aligned with first video DTS then we need to take that into account
  4389. // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small
  4390. // drift between audio and video streams
  4391. var audiovideoDeltaDts = (audioTrack.samples[0].dts - videoTrack.samples[0].dts) / videoTrack.inputTimeScale;
  4392. audioTimeOffset += Math.max(0, audiovideoDeltaDts);
  4393. videoTimeOffset += Math.max(0, -audiovideoDeltaDts);
  4394. }
  4395. // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is
  4396. // calculated in remuxAudio.
  4397. //logger.log('nb AAC samples:' + audioTrack.samples.length);
  4398. if (nbAudioSamples) {
  4399. // if initSegment was generated without video samples, regenerate it again
  4400. if (!audioTrack.timescale) {
  4401. logger["b" /* logger */].warn('regenerate InitSegment as audio detected');
  4402. this.generateIS(audioTrack, videoTrack, timeOffset);
  4403. }
  4404. var audioData = this.remuxAudio(audioTrack, audioTimeOffset, contiguous, accurateTimeOffset);
  4405. //logger.log('nb AVC samples:' + videoTrack.samples.length);
  4406. if (nbVideoSamples) {
  4407. var audioTrackLength = void 0;
  4408. if (audioData) {
  4409. audioTrackLength = audioData.endPTS - audioData.startPTS;
  4410. }
  4411. // if initSegment was generated without video samples, regenerate it again
  4412. if (!videoTrack.timescale) {
  4413. logger["b" /* logger */].warn('regenerate InitSegment as video detected');
  4414. this.generateIS(audioTrack, videoTrack, timeOffset);
  4415. }
  4416. this.remuxVideo(videoTrack, videoTimeOffset, contiguous, audioTrackLength, accurateTimeOffset);
  4417. }
  4418. } else {
  4419. var videoData = void 0;
  4420. //logger.log('nb AVC samples:' + videoTrack.samples.length);
  4421. if (nbVideoSamples) {
  4422. videoData = this.remuxVideo(videoTrack, videoTimeOffset, contiguous, accurateTimeOffset);
  4423. }
  4424. if (videoData && audioTrack.codec) {
  4425. this.remuxEmptyAudio(audioTrack, audioTimeOffset, contiguous, videoData);
  4426. }
  4427. }
  4428. }
  4429. //logger.log('nb ID3 samples:' + audioTrack.samples.length);
  4430. if (id3Track.samples.length) {
  4431. this.remuxID3(id3Track, timeOffset);
  4432. }
  4433. //logger.log('nb ID3 samples:' + audioTrack.samples.length);
  4434. if (textTrack.samples.length) {
  4435. this.remuxText(textTrack, timeOffset);
  4436. }
  4437. //notify end of parsing
  4438. this.observer.trigger(events["a" /* default */].FRAG_PARSED);
  4439. };
  4440. MP4Remuxer.prototype.generateIS = function generateIS(audioTrack, videoTrack, timeOffset) {
  4441. var observer = this.observer,
  4442. audioSamples = audioTrack.samples,
  4443. videoSamples = videoTrack.samples,
  4444. typeSupported = this.typeSupported,
  4445. container = 'audio/mp4',
  4446. tracks = {},
  4447. data = {tracks: tracks},
  4448. computePTSDTS = this._initPTS === undefined,
  4449. initPTS,
  4450. initDTS;
  4451. if (computePTSDTS) {
  4452. initPTS = initDTS = Infinity;
  4453. }
  4454. if (audioTrack.config && audioSamples.length) {
  4455. // let's use audio sampling rate as MP4 time scale.
  4456. // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC)
  4457. // using audio sampling rate here helps having an integer MP4 frame duration
  4458. // this avoids potential rounding issue and AV sync issue
  4459. audioTrack.timescale = audioTrack.samplerate;
  4460. logger["b" /* logger */].log('audio sampling rate : ' + audioTrack.samplerate);
  4461. if (!audioTrack.isAAC) {
  4462. if (typeSupported.mpeg) {
  4463. // Chrome and Safari
  4464. container = 'audio/mpeg';
  4465. audioTrack.codec = '';
  4466. } else if (typeSupported.mp3) {
  4467. // Firefox
  4468. audioTrack.codec = 'mp3';
  4469. }
  4470. }
  4471. tracks.audio = {
  4472. container: container,
  4473. codec: audioTrack.codec,
  4474. initSegment: !audioTrack.isAAC && typeSupported.mpeg ? new Uint8Array() : mp4_generator.initSegment([audioTrack]),
  4475. metadata: {
  4476. channelCount: audioTrack.channelCount
  4477. }
  4478. };
  4479. if (computePTSDTS) {
  4480. // remember first PTS of this demuxing context. for audio, PTS = DTS
  4481. initPTS = initDTS = audioSamples[0].pts - audioTrack.inputTimeScale * timeOffset;
  4482. }
  4483. }
  4484. if (videoTrack.sps && videoTrack.pps && videoSamples.length) {
  4485. // let's use input time scale as MP4 video timescale
  4486. // we use input time scale straight away to avoid rounding issues on frame duration / cts computation
  4487. var inputTimeScale = videoTrack.inputTimeScale;
  4488. videoTrack.timescale = inputTimeScale;
  4489. tracks.video = {
  4490. container: 'video/mp4',
  4491. codec: videoTrack.codec,
  4492. initSegment: mp4_generator.initSegment([videoTrack]),
  4493. metadata: {
  4494. width: videoTrack.width,
  4495. height: videoTrack.height
  4496. }
  4497. };
  4498. if (computePTSDTS) {
  4499. initPTS = Math.min(initPTS, videoSamples[0].pts - inputTimeScale * timeOffset);
  4500. initDTS = Math.min(initDTS, videoSamples[0].dts - inputTimeScale * timeOffset);
  4501. this.observer.trigger(events["a" /* default */].INIT_PTS_FOUND, {initPTS: initPTS});
  4502. }
  4503. }
  4504. if (Object.keys(tracks).length) {
  4505. observer.trigger(events["a" /* default */].FRAG_PARSING_INIT_SEGMENT, data);
  4506. this.ISGenerated = true;
  4507. if (computePTSDTS) {
  4508. this._initPTS = initPTS;
  4509. this._initDTS = initDTS;
  4510. }
  4511. } else {
  4512. observer.trigger(events["a" /* default */].ERROR, {
  4513. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  4514. details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR,
  4515. fatal: false,
  4516. reason: 'no audio/video samples found'
  4517. });
  4518. }
  4519. };
  4520. MP4Remuxer.prototype.remuxVideo = function remuxVideo(track, timeOffset, contiguous, audioTrackLength, accurateTimeOffset) {
  4521. var offset = 8,
  4522. timeScale = track.timescale,
  4523. mp4SampleDuration,
  4524. mdat,
  4525. moof,
  4526. firstPTS,
  4527. firstDTS,
  4528. nextDTS,
  4529. lastPTS,
  4530. lastDTS,
  4531. inputSamples = track.samples,
  4532. outputSamples = [],
  4533. nbSamples = inputSamples.length,
  4534. ptsNormalize = this._PTSNormalize,
  4535. initDTS = this._initDTS;
  4536. // for (let i = 0; i < track.samples.length; i++) {
  4537. // let avcSample = track.samples[i];
  4538. // let units = avcSample.units;
  4539. // let unitsString = '';
  4540. // for (let j = 0; j < units.length ; j++) {
  4541. // unitsString += units[j].type + ',';
  4542. // if (units[j].data.length < 500) {
  4543. // unitsString += Hex.hexDump(units[j].data);
  4544. // }
  4545. // }
  4546. // logger.log(avcSample.pts + '/' + avcSample.dts + ',' + unitsString + avcSample.units.length);
  4547. // }
  4548. // if parsed fragment is contiguous with last one, let's use last DTS value as reference
  4549. var nextAvcDts = this.nextAvcDts;
  4550. var isSafari = this.isSafari;
  4551. // Safari does not like overlapping DTS on consecutive fragments. let's use nextAvcDts to overcome this if fragments are consecutive
  4552. if (isSafari) {
  4553. // also consider consecutive fragments as being contiguous (even if a level switch occurs),
  4554. // for sake of clarity:
  4555. // consecutive fragments are frags with
  4556. // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR
  4557. // - less than 200 ms PTS gaps (timeScale/5)
  4558. contiguous |= inputSamples.length && nextAvcDts && (accurateTimeOffset && Math.abs(timeOffset - nextAvcDts / timeScale) < 0.1 || Math.abs(inputSamples[0].pts - nextAvcDts - initDTS) < timeScale / 5);
  4559. }
  4560. if (!contiguous) {
  4561. // if not contiguous, let's use target timeOffset
  4562. nextAvcDts = timeOffset * timeScale;
  4563. }
  4564. // PTS is coded on 33bits, and can loop from -2^32 to 2^32
  4565. // ptsNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value
  4566. inputSamples.forEach(function (sample) {
  4567. sample.pts = ptsNormalize(sample.pts - initDTS, nextAvcDts);
  4568. sample.dts = ptsNormalize(sample.dts - initDTS, nextAvcDts);
  4569. });
  4570. // sort video samples by DTS then PTS then demux id order
  4571. inputSamples.sort(function (a, b) {
  4572. var deltadts = a.dts - b.dts;
  4573. var deltapts = a.pts - b.pts;
  4574. return deltadts ? deltadts : deltapts ? deltapts : a.id - b.id;
  4575. });
  4576. // handle broken streams with PTS < DTS, tolerance up 200ms (18000 in 90kHz timescale)
  4577. var PTSDTSshift = inputSamples.reduce(function (prev, curr) {
  4578. return Math.max(Math.min(prev, curr.pts - curr.dts), -18000);
  4579. }, 0);
  4580. if (PTSDTSshift < 0) {
  4581. logger["b" /* logger */].warn('PTS < DTS detected in video samples, shifting DTS by ' + Math.round(PTSDTSshift / 90) + ' ms to overcome this issue');
  4582. for (var i = 0; i < inputSamples.length; i++) {
  4583. inputSamples[i].dts += PTSDTSshift;
  4584. }
  4585. }
  4586. // compute first DTS and last DTS, normalize them against reference value
  4587. var sample = inputSamples[0];
  4588. firstDTS = Math.max(sample.dts, 0);
  4589. firstPTS = Math.max(sample.pts, 0);
  4590. // check timestamp continuity accross consecutive fragments (this is to remove inter-fragment gap/hole)
  4591. var delta = Math.round((firstDTS - nextAvcDts) / 90);
  4592. // if fragment are contiguous, detect hole/overlapping between fragments
  4593. if (contiguous) {
  4594. if (delta) {
  4595. if (delta > 1) {
  4596. logger["b" /* logger */].log('AVC:' + delta + ' ms hole between fragments detected,filling it');
  4597. } else if (delta < -1) {
  4598. logger["b" /* logger */].log('AVC:' + -delta + ' ms overlapping between fragments detected');
  4599. }
  4600. // remove hole/gap : set DTS to next expected DTS
  4601. firstDTS = nextAvcDts;
  4602. inputSamples[0].dts = firstDTS;
  4603. // offset PTS as well, ensure that PTS is smaller or equal than new DTS
  4604. firstPTS = Math.max(firstPTS - delta, nextAvcDts);
  4605. inputSamples[0].pts = firstPTS;
  4606. logger["b" /* logger */].log('Video/PTS/DTS adjusted: ' + Math.round(firstPTS / 90) + '/' + Math.round(firstDTS / 90) + ',delta:' + delta + ' ms');
  4607. }
  4608. }
  4609. nextDTS = firstDTS;
  4610. // compute lastPTS/lastDTS
  4611. sample = inputSamples[inputSamples.length - 1];
  4612. lastDTS = Math.max(sample.dts, 0);
  4613. lastPTS = Math.max(sample.pts, 0, lastDTS);
  4614. // on Safari let's signal the same sample duration for all samples
  4615. // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS
  4616. // set this constant duration as being the avg delta between consecutive DTS.
  4617. if (isSafari) {
  4618. mp4SampleDuration = Math.round((lastDTS - firstDTS) / (inputSamples.length - 1));
  4619. }
  4620. var nbNalu = 0,
  4621. naluLen = 0;
  4622. for (var _i = 0; _i < nbSamples; _i++) {
  4623. // compute total/avc sample length and nb of NAL units
  4624. var _sample = inputSamples[_i],
  4625. units = _sample.units,
  4626. nbUnits = units.length,
  4627. sampleLen = 0;
  4628. for (var j = 0; j < nbUnits; j++) {
  4629. sampleLen += units[j].data.length;
  4630. }
  4631. naluLen += sampleLen;
  4632. nbNalu += nbUnits;
  4633. _sample.length = sampleLen;
  4634. // normalize PTS/DTS
  4635. if (isSafari) {
  4636. // sample DTS is computed using a constant decoding offset (mp4SampleDuration) between samples
  4637. _sample.dts = firstDTS + _i * mp4SampleDuration;
  4638. } else {
  4639. // ensure sample monotonic DTS
  4640. _sample.dts = Math.max(_sample.dts, firstDTS);
  4641. }
  4642. // ensure that computed value is greater or equal than sample DTS
  4643. _sample.pts = Math.max(_sample.pts, _sample.dts);
  4644. }
  4645. /* concatenate the video data and construct the mdat in place
  4646. (need 8 more bytes to fill length and mpdat type) */
  4647. var mdatSize = naluLen + 4 * nbNalu + 8;
  4648. try {
  4649. mdat = new Uint8Array(mdatSize);
  4650. } catch (err) {
  4651. this.observer.trigger(events["a" /* default */].ERROR, {
  4652. type: errors["b" /* ErrorTypes */].MUX_ERROR,
  4653. details: errors["a" /* ErrorDetails */].REMUX_ALLOC_ERROR,
  4654. fatal: false,
  4655. bytes: mdatSize,
  4656. reason: 'fail allocating video mdat ' + mdatSize
  4657. });
  4658. return;
  4659. }
  4660. var view = new DataView(mdat.buffer);
  4661. view.setUint32(0, mdatSize);
  4662. mdat.set(mp4_generator.types.mdat, 4);
  4663. for (var _i2 = 0; _i2 < nbSamples; _i2++) {
  4664. var avcSample = inputSamples[_i2],
  4665. avcSampleUnits = avcSample.units,
  4666. mp4SampleLength = 0,
  4667. compositionTimeOffset = void 0;
  4668. // convert NALU bitstream to MP4 format (prepend NALU with size field)
  4669. for (var _j = 0, _nbUnits = avcSampleUnits.length; _j < _nbUnits; _j++) {
  4670. var unit = avcSampleUnits[_j],
  4671. unitData = unit.data,
  4672. unitDataLen = unit.data.byteLength;
  4673. view.setUint32(offset, unitDataLen);
  4674. offset += 4;
  4675. mdat.set(unitData, offset);
  4676. offset += unitDataLen;
  4677. mp4SampleLength += 4 + unitDataLen;
  4678. }
  4679. if (!isSafari) {
  4680. // expected sample duration is the Decoding Timestamp diff of consecutive samples
  4681. if (_i2 < nbSamples - 1) {
  4682. mp4SampleDuration = inputSamples[_i2 + 1].dts - avcSample.dts;
  4683. } else {
  4684. var config = this.config,
  4685. lastFrameDuration = avcSample.dts - inputSamples[_i2 > 0 ? _i2 - 1 : _i2].dts;
  4686. if (config.stretchShortVideoTrack) {
  4687. // In some cases, a segment's audio track duration may exceed the video track duration.
  4688. // Since we've already remuxed audio, and we know how long the audio track is, we look to
  4689. // see if the delta to the next segment is longer than the minimum of maxBufferHole and
  4690. // maxSeekHole. If so, playback would potentially get stuck, so we artificially inflate
  4691. // the duration of the last frame to minimize any potential gap between segments.
  4692. var maxBufferHole = config.maxBufferHole,
  4693. maxSeekHole = config.maxSeekHole,
  4694. gapTolerance = Math.floor(Math.min(maxBufferHole, maxSeekHole) * timeScale),
  4695. deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts;
  4696. if (deltaToFrameEnd > gapTolerance) {
  4697. // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video
  4698. // frame overlap. maxBufferHole/maxSeekHole should be >> lastFrameDuration anyway.
  4699. mp4SampleDuration = deltaToFrameEnd - lastFrameDuration;
  4700. if (mp4SampleDuration < 0) {
  4701. mp4SampleDuration = lastFrameDuration;
  4702. }
  4703. logger["b" /* logger */].log('It is approximately ' + deltaToFrameEnd / 90 + ' ms to the next segment; using duration ' + mp4SampleDuration / 90 + ' ms for the last video frame.');
  4704. } else {
  4705. mp4SampleDuration = lastFrameDuration;
  4706. }
  4707. } else {
  4708. mp4SampleDuration = lastFrameDuration;
  4709. }
  4710. }
  4711. compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts);
  4712. } else {
  4713. compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / mp4SampleDuration));
  4714. }
  4715. //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${avcSample.pts}/${avcSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(avcSample.pts/4294967296).toFixed(3)}');
  4716. outputSamples.push({
  4717. size: mp4SampleLength,
  4718. // constant duration
  4719. duration: mp4SampleDuration,
  4720. cts: compositionTimeOffset,
  4721. flags: {
  4722. isLeading: 0,
  4723. isDependedOn: 0,
  4724. hasRedundancy: 0,
  4725. degradPrio: 0,
  4726. dependsOn: avcSample.key ? 2 : 1,
  4727. isNonSync: avcSample.key ? 0 : 1
  4728. }
  4729. });
  4730. }
  4731. // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
  4732. this.nextAvcDts = lastDTS + mp4SampleDuration;
  4733. var dropped = track.dropped;
  4734. track.len = 0;
  4735. track.nbNalu = 0;
  4736. track.dropped = 0;
  4737. if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
  4738. var flags = outputSamples[0].flags;
  4739. // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
  4740. // https://code.google.com/p/chromium/issues/detail?id=229412
  4741. flags.dependsOn = 2;
  4742. flags.isNonSync = 0;
  4743. }
  4744. track.samples = outputSamples;
  4745. moof = mp4_generator.moof(track.sequenceNumber++, firstDTS, track);
  4746. track.samples = [];
  4747. var data = {
  4748. data1: moof,
  4749. data2: mdat,
  4750. startPTS: firstPTS / timeScale,
  4751. endPTS: (lastPTS + mp4SampleDuration) / timeScale,
  4752. startDTS: firstDTS / timeScale,
  4753. endDTS: this.nextAvcDts / timeScale,
  4754. type: 'video',
  4755. nb: outputSamples.length,
  4756. dropped: dropped
  4757. };
  4758. this.observer.trigger(events["a" /* default */].FRAG_PARSING_DATA, data);
  4759. return data;
  4760. };
  4761. MP4Remuxer.prototype.remuxAudio = function remuxAudio(track, timeOffset, contiguous, accurateTimeOffset) {
  4762. var inputTimeScale = track.inputTimeScale,
  4763. mp4timeScale = track.timescale,
  4764. scaleFactor = inputTimeScale / mp4timeScale,
  4765. mp4SampleDuration = track.isAAC ? 1024 : 1152,
  4766. inputSampleDuration = mp4SampleDuration * scaleFactor,
  4767. ptsNormalize = this._PTSNormalize,
  4768. initDTS = this._initDTS,
  4769. rawMPEG = !track.isAAC && this.typeSupported.mpeg;
  4770. var offset,
  4771. mp4Sample,
  4772. fillFrame,
  4773. mdat,
  4774. moof,
  4775. firstPTS,
  4776. lastPTS,
  4777. inputSamples = track.samples,
  4778. outputSamples = [],
  4779. nextAudioPts = this.nextAudioPts;
  4780. // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs),
  4781. // for sake of clarity:
  4782. // consecutive fragments are frags with
  4783. // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR
  4784. // - less than 20 audio frames distance
  4785. // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1)
  4786. // this helps ensuring audio continuity
  4787. // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame
  4788. contiguous |= inputSamples.length && nextAudioPts && (accurateTimeOffset && Math.abs(timeOffset - nextAudioPts / inputTimeScale) < 0.1 || Math.abs(inputSamples[0].pts - nextAudioPts - initDTS) < 20 * inputSampleDuration);
  4789. // compute normalized PTS
  4790. inputSamples.forEach(function (sample) {
  4791. sample.pts = sample.dts = ptsNormalize(sample.pts - initDTS, timeOffset * inputTimeScale);
  4792. });
  4793. // filter out sample with negative PTS that are not playable anyway
  4794. // if we don't remove these negative samples, they will shift all audio samples forward.
  4795. // leading to audio overlap between current / next fragment
  4796. inputSamples = inputSamples.filter(function (sample) {
  4797. return sample.pts >= 0;
  4798. });
  4799. // in case all samples have negative PTS, and have been filtered out, return now
  4800. if (inputSamples.length === 0) {
  4801. return;
  4802. }
  4803. if (!contiguous) {
  4804. if (!accurateTimeOffset) {
  4805. // if frag are mot contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS
  4806. nextAudioPts = inputSamples[0].pts;
  4807. } else {
  4808. // if timeOffset is accurate, let's use it as predicted next audio PTS
  4809. nextAudioPts = timeOffset * inputTimeScale;
  4810. }
  4811. }
  4812. // If the audio track is missing samples, the frames seem to get "left-shifted" within the
  4813. // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment.
  4814. // In an effort to prevent this from happening, we inject frames here where there are gaps.
  4815. // When possible, we inject a silent frame; when that's not possible, we duplicate the last
  4816. // frame.
  4817. if (track.isAAC) {
  4818. var maxAudioFramesDrift = this.config.maxAudioFramesDrift;
  4819. for (var i = 0, nextPts = nextAudioPts; i < inputSamples.length;) {
  4820. // First, let's see how far off this frame is from where we expect it to be
  4821. var sample = inputSamples[i],
  4822. delta;
  4823. var pts = sample.pts;
  4824. delta = pts - nextPts;
  4825. var duration = Math.abs(1000 * delta / inputTimeScale);
  4826. // If we're overlapping by more than a duration, drop this sample
  4827. if (delta <= -maxAudioFramesDrift * inputSampleDuration) {
  4828. logger["b" /* logger */].warn('Dropping 1 audio frame @ ' + (nextPts / inputTimeScale).toFixed(3) + 's due to ' + Math.round(duration) + ' ms overlap.');
  4829. inputSamples.splice(i, 1);
  4830. track.len -= sample.unit.length;
  4831. // Don't touch nextPtsNorm or i
  4832. }
  4833. // Insert missing frames if:
  4834. // 1: We're more than maxAudioFramesDrift frame away
  4835. // 2: Not more than MAX_SILENT_FRAME_DURATION away
  4836. // 3: currentTime (aka nextPtsNorm) is not 0
  4837. else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && nextPts) {
  4838. var missing = Math.round(delta / inputSampleDuration);
  4839. logger["b" /* logger */].warn('Injecting ' + missing + ' audio frame @ ' + (nextPts / inputTimeScale).toFixed(3) + 's due to ' + Math.round(1000 * delta / inputTimeScale) + ' ms gap.');
  4840. for (var j = 0; j < missing; j++) {
  4841. var newStamp = Math.max(nextPts, 0);
  4842. fillFrame = aac.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
  4843. if (!fillFrame) {
  4844. logger["b" /* logger */].log('Unable to get silent frame for given audio codec; duplicating last frame instead.');
  4845. fillFrame = sample.unit.subarray();
  4846. }
  4847. inputSamples.splice(i, 0, {unit: fillFrame, pts: newStamp, dts: newStamp});
  4848. track.len += fillFrame.length;
  4849. nextPts += inputSampleDuration;
  4850. i++;
  4851. }
  4852. // Adjust sample to next expected pts
  4853. sample.pts = sample.dts = nextPts;
  4854. nextPts += inputSampleDuration;
  4855. i++;
  4856. } else {
  4857. // Otherwise, just adjust pts
  4858. if (Math.abs(delta) > 0.1 * inputSampleDuration) {
  4859. //logger.log(`Invalid frame delta ${Math.round(delta + inputSampleDuration)} at PTS ${Math.round(pts / 90)} (should be ${Math.round(inputSampleDuration)}).`);
  4860. }
  4861. sample.pts = sample.dts = nextPts;
  4862. nextPts += inputSampleDuration;
  4863. i++;
  4864. }
  4865. }
  4866. }
  4867. for (var _j2 = 0, _nbSamples = inputSamples.length; _j2 < _nbSamples; _j2++) {
  4868. var audioSample = inputSamples[_j2];
  4869. var unit = audioSample.unit;
  4870. var _pts = audioSample.pts;
  4871. //logger.log(`Audio/PTS:${Math.round(pts/90)}`);
  4872. // if not first sample
  4873. if (lastPTS !== undefined) {
  4874. mp4Sample.duration = Math.round((_pts - lastPTS) / scaleFactor);
  4875. } else {
  4876. var _delta = Math.round(1000 * (_pts - nextAudioPts) / inputTimeScale),
  4877. numMissingFrames = 0;
  4878. // if fragment are contiguous, detect hole/overlapping between fragments
  4879. // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1)
  4880. if (contiguous && track.isAAC) {
  4881. // log delta
  4882. if (_delta) {
  4883. if (_delta > 0 && _delta < MAX_SILENT_FRAME_DURATION) {
  4884. numMissingFrames = Math.round((_pts - nextAudioPts) / inputSampleDuration);
  4885. logger["b" /* logger */].log(_delta + ' ms hole between AAC samples detected,filling it');
  4886. if (numMissingFrames > 0) {
  4887. fillFrame = aac.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
  4888. if (!fillFrame) {
  4889. fillFrame = unit.subarray();
  4890. }
  4891. track.len += numMissingFrames * fillFrame.length;
  4892. }
  4893. // if we have frame overlap, overlapping for more than half a frame duraion
  4894. } else if (_delta < -12) {
  4895. // drop overlapping audio frames... browser will deal with it
  4896. logger["b" /* logger */].log('drop overlapping AAC sample, expected/parsed/delta:' + (nextAudioPts / inputTimeScale).toFixed(3) + 's/' + (_pts / inputTimeScale).toFixed(3) + 's/' + -_delta + 'ms');
  4897. track.len -= unit.byteLength;
  4898. continue;
  4899. }
  4900. // set PTS/DTS to expected PTS/DTS
  4901. _pts = nextAudioPts;
  4902. }
  4903. }
  4904. // remember first PTS of our audioSamples
  4905. firstPTS = _pts;
  4906. if (track.len > 0) {
  4907. /* concatenate the audio data and construct the mdat in place
  4908. (need 8 more bytes to fill length and mdat type) */
  4909. var mdatSize = rawMPEG ? track.len : track.len + 8;
  4910. offset = rawMPEG ? 0 : 8;
  4911. try {
  4912. mdat = new Uint8Array(mdatSize);
  4913. } catch (err) {
  4914. this.observer.trigger(events["a" /* default */].ERROR, {
  4915. type: errors["b" /* ErrorTypes */].MUX_ERROR,
  4916. details: errors["a" /* ErrorDetails */].REMUX_ALLOC_ERROR,
  4917. fatal: false,
  4918. bytes: mdatSize,
  4919. reason: 'fail allocating audio mdat ' + mdatSize
  4920. });
  4921. return;
  4922. }
  4923. if (!rawMPEG) {
  4924. var view = new DataView(mdat.buffer);
  4925. view.setUint32(0, mdatSize);
  4926. mdat.set(mp4_generator.types.mdat, 4);
  4927. }
  4928. } else {
  4929. // no audio samples
  4930. return;
  4931. }
  4932. for (var _i3 = 0; _i3 < numMissingFrames; _i3++) {
  4933. fillFrame = aac.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
  4934. if (!fillFrame) {
  4935. logger["b" /* logger */].log('Unable to get silent frame for given audio codec; duplicating this frame instead.');
  4936. fillFrame = unit.subarray();
  4937. }
  4938. mdat.set(fillFrame, offset);
  4939. offset += fillFrame.byteLength;
  4940. mp4Sample = {
  4941. size: fillFrame.byteLength,
  4942. cts: 0,
  4943. duration: 1024,
  4944. flags: {
  4945. isLeading: 0,
  4946. isDependedOn: 0,
  4947. hasRedundancy: 0,
  4948. degradPrio: 0,
  4949. dependsOn: 1
  4950. }
  4951. };
  4952. outputSamples.push(mp4Sample);
  4953. }
  4954. }
  4955. mdat.set(unit, offset);
  4956. var unitLen = unit.byteLength;
  4957. offset += unitLen;
  4958. //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${audioSample.pts}/${audioSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(audioSample.pts/4294967296).toFixed(3)}');
  4959. mp4Sample = {
  4960. size: unitLen,
  4961. cts: 0,
  4962. duration: 0,
  4963. flags: {
  4964. isLeading: 0,
  4965. isDependedOn: 0,
  4966. hasRedundancy: 0,
  4967. degradPrio: 0,
  4968. dependsOn: 1
  4969. }
  4970. };
  4971. outputSamples.push(mp4Sample);
  4972. lastPTS = _pts;
  4973. }
  4974. var lastSampleDuration = 0;
  4975. var nbSamples = outputSamples.length;
  4976. //set last sample duration as being identical to previous sample
  4977. if (nbSamples >= 2) {
  4978. lastSampleDuration = outputSamples[nbSamples - 2].duration;
  4979. mp4Sample.duration = lastSampleDuration;
  4980. }
  4981. if (nbSamples) {
  4982. // next audio sample PTS should be equal to last sample PTS + duration
  4983. this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSampleDuration;
  4984. //logger.log('Audio/PTS/PTSend:' + audioSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0));
  4985. track.len = 0;
  4986. track.samples = outputSamples;
  4987. if (rawMPEG) {
  4988. moof = new Uint8Array();
  4989. } else {
  4990. moof = mp4_generator.moof(track.sequenceNumber++, firstPTS / scaleFactor, track);
  4991. }
  4992. track.samples = [];
  4993. var start = firstPTS / inputTimeScale;
  4994. var end = nextAudioPts / inputTimeScale;
  4995. var audioData = {
  4996. data1: moof,
  4997. data2: mdat,
  4998. startPTS: start,
  4999. endPTS: end,
  5000. startDTS: start,
  5001. endDTS: end,
  5002. type: 'audio',
  5003. nb: nbSamples
  5004. };
  5005. this.observer.trigger(events["a" /* default */].FRAG_PARSING_DATA, audioData);
  5006. return audioData;
  5007. }
  5008. return null;
  5009. };
  5010. MP4Remuxer.prototype.remuxEmptyAudio = function remuxEmptyAudio(track, timeOffset, contiguous, videoData) {
  5011. var inputTimeScale = track.inputTimeScale,
  5012. mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale,
  5013. scaleFactor = inputTimeScale / mp4timeScale,
  5014. nextAudioPts = this.nextAudioPts,
  5015. // sync with video's timestamp
  5016. startDTS = (nextAudioPts !== undefined ? nextAudioPts : videoData.startDTS * inputTimeScale) + this._initDTS,
  5017. endDTS = videoData.endDTS * inputTimeScale + this._initDTS,
  5018. // one sample's duration value
  5019. sampleDuration = 1024,
  5020. frameDuration = scaleFactor * sampleDuration,
  5021. // samples count of this segment's duration
  5022. nbSamples = Math.ceil((endDTS - startDTS) / frameDuration),
  5023. // silent frame
  5024. silentFrame = aac.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
  5025. logger["b" /* logger */].warn('remux empty Audio');
  5026. // Can't remux if we can't generate a silent frame...
  5027. if (!silentFrame) {
  5028. logger["b" /* logger */].trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!');
  5029. return;
  5030. }
  5031. var samples = [];
  5032. for (var i = 0; i < nbSamples; i++) {
  5033. var stamp = startDTS + i * frameDuration;
  5034. samples.push({unit: silentFrame, pts: stamp, dts: stamp});
  5035. track.len += silentFrame.length;
  5036. }
  5037. track.samples = samples;
  5038. this.remuxAudio(track, timeOffset, contiguous);
  5039. };
  5040. MP4Remuxer.prototype.remuxID3 = function remuxID3(track, timeOffset) {
  5041. var length = track.samples.length,
  5042. sample;
  5043. var inputTimeScale = track.inputTimeScale;
  5044. var initPTS = this._initPTS;
  5045. var initDTS = this._initDTS;
  5046. // consume samples
  5047. if (length) {
  5048. for (var index = 0; index < length; index++) {
  5049. sample = track.samples[index];
  5050. // setting id3 pts, dts to relative time
  5051. // using this._initPTS and this._initDTS to calculate relative time
  5052. sample.pts = (sample.pts - initPTS) / inputTimeScale;
  5053. sample.dts = (sample.dts - initDTS) / inputTimeScale;
  5054. }
  5055. this.observer.trigger(events["a" /* default */].FRAG_PARSING_METADATA, {
  5056. samples: track.samples
  5057. });
  5058. }
  5059. track.samples = [];
  5060. timeOffset = timeOffset;
  5061. };
  5062. MP4Remuxer.prototype.remuxText = function remuxText(track, timeOffset) {
  5063. track.samples.sort(function (a, b) {
  5064. return a.pts - b.pts;
  5065. });
  5066. var length = track.samples.length,
  5067. sample;
  5068. var inputTimeScale = track.inputTimeScale;
  5069. var initPTS = this._initPTS;
  5070. // consume samples
  5071. if (length) {
  5072. for (var index = 0; index < length; index++) {
  5073. sample = track.samples[index];
  5074. // setting text pts, dts to relative time
  5075. // using this._initPTS and this._initDTS to calculate relative time
  5076. sample.pts = (sample.pts - initPTS) / inputTimeScale;
  5077. }
  5078. this.observer.trigger(events["a" /* default */].FRAG_PARSING_USERDATA, {
  5079. samples: track.samples
  5080. });
  5081. }
  5082. track.samples = [];
  5083. timeOffset = timeOffset;
  5084. };
  5085. MP4Remuxer.prototype._PTSNormalize = function _PTSNormalize(value, reference) {
  5086. var offset;
  5087. if (reference === undefined) {
  5088. return value;
  5089. }
  5090. if (reference < value) {
  5091. // - 2^33
  5092. offset = -8589934592;
  5093. } else {
  5094. // + 2^33
  5095. offset = 8589934592;
  5096. }
  5097. /* PTS is 33bit (from 0 to 2^33 -1)
  5098. if diff between value and reference is bigger than half of the amplitude (2^32) then it means that
  5099. PTS looping occured. fill the gap */
  5100. while (Math.abs(value - reference) > 4294967296) {
  5101. value += offset;
  5102. }
  5103. return value;
  5104. };
  5105. return MP4Remuxer;
  5106. }();
  5107. /* harmony default export */
  5108. var mp4_remuxer = (mp4_remuxer_MP4Remuxer);
  5109. // CONCATENATED MODULE: ./src/remux/passthrough-remuxer.js
  5110. function passthrough_remuxer__classCallCheck(instance, Constructor) {
  5111. if (!(instance instanceof Constructor)) {
  5112. throw new TypeError("Cannot call a class as a function");
  5113. }
  5114. }
  5115. /**
  5116. * passthrough remuxer
  5117. */
  5118. var passthrough_remuxer_PassThroughRemuxer = function () {
  5119. function PassThroughRemuxer(observer) {
  5120. passthrough_remuxer__classCallCheck(this, PassThroughRemuxer);
  5121. this.observer = observer;
  5122. }
  5123. PassThroughRemuxer.prototype.destroy = function destroy() {
  5124. };
  5125. PassThroughRemuxer.prototype.resetTimeStamp = function resetTimeStamp() {
  5126. };
  5127. PassThroughRemuxer.prototype.resetInitSegment = function resetInitSegment() {
  5128. };
  5129. PassThroughRemuxer.prototype.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset, rawData) {
  5130. var observer = this.observer;
  5131. var streamType = '';
  5132. if (audioTrack) {
  5133. streamType += 'audio';
  5134. }
  5135. if (videoTrack) {
  5136. streamType += 'video';
  5137. }
  5138. observer.trigger(events["a" /* default */].FRAG_PARSING_DATA, {
  5139. data1: rawData,
  5140. startPTS: timeOffset,
  5141. startDTS: timeOffset,
  5142. type: streamType,
  5143. nb: 1,
  5144. dropped: 0
  5145. });
  5146. //notify end of parsing
  5147. observer.trigger(events["a" /* default */].FRAG_PARSED);
  5148. };
  5149. return PassThroughRemuxer;
  5150. }();
  5151. /* harmony default export */
  5152. var passthrough_remuxer = (passthrough_remuxer_PassThroughRemuxer);
  5153. // CONCATENATED MODULE: ./src/demux/demuxer-inline.js
  5154. function demuxer_inline__classCallCheck(instance, Constructor) {
  5155. if (!(instance instanceof Constructor)) {
  5156. throw new TypeError("Cannot call a class as a function");
  5157. }
  5158. }
  5159. /* inline demuxer.
  5160. * probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
  5161. */
  5162. var demuxer_inline_DemuxerInline = function () {
  5163. function DemuxerInline(observer, typeSupported, config, vendor) {
  5164. demuxer_inline__classCallCheck(this, DemuxerInline);
  5165. this.observer = observer;
  5166. this.typeSupported = typeSupported;
  5167. this.config = config;
  5168. this.vendor = vendor;
  5169. }
  5170. DemuxerInline.prototype.destroy = function destroy() {
  5171. var demuxer = this.demuxer;
  5172. if (demuxer) {
  5173. demuxer.destroy();
  5174. }
  5175. };
  5176. DemuxerInline.prototype.push = function push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) {
  5177. if (data.byteLength > 0 && decryptdata != null && decryptdata.key != null && decryptdata.method === 'AES-128') {
  5178. var decrypter = this.decrypter;
  5179. if (decrypter == null) {
  5180. decrypter = this.decrypter = new crypt_decrypter["a" /* default */](this.observer, this.config);
  5181. }
  5182. var localthis = this;
  5183. // performance.now() not available on WebWorker, at least on Safari Desktop
  5184. var startTime;
  5185. try {
  5186. startTime = performance.now();
  5187. } catch (error) {
  5188. startTime = Date.now();
  5189. }
  5190. decrypter.decrypt(data, decryptdata.key.buffer, decryptdata.iv.buffer, function (decryptedData) {
  5191. var endTime;
  5192. try {
  5193. endTime = performance.now();
  5194. } catch (error) {
  5195. endTime = Date.now();
  5196. }
  5197. localthis.observer.trigger(events["a" /* default */].FRAG_DECRYPTED, {
  5198. stats: {
  5199. tstart: startTime,
  5200. tdecrypt: endTime
  5201. }
  5202. });
  5203. localthis.pushDecrypted(new Uint8Array(decryptedData), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
  5204. });
  5205. } else {
  5206. this.pushDecrypted(new Uint8Array(data), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
  5207. }
  5208. };
  5209. DemuxerInline.prototype.pushDecrypted = function pushDecrypted(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) {
  5210. var demuxer = this.demuxer;
  5211. if (!demuxer ||
  5212. // in case of continuity change, we might switch from content type (AAC container to TS container for example)
  5213. // so let's check that current demuxer is still valid
  5214. discontinuity && !this.probe(data)) {
  5215. var observer = this.observer;
  5216. var typeSupported = this.typeSupported;
  5217. var config = this.config;
  5218. // probing order is TS/AAC/MP3/MP4
  5219. var muxConfig = [{
  5220. demux: tsdemuxer,
  5221. remux: mp4_remuxer
  5222. }, {demux: mp4demuxer["a" /* default */], remux: passthrough_remuxer}, {
  5223. demux: aacdemuxer,
  5224. remux: mp4_remuxer
  5225. }, {demux: mp3demuxer, remux: mp4_remuxer}];
  5226. // probe for content type
  5227. for (var i = 0, len = muxConfig.length; i < len; i++) {
  5228. var mux = muxConfig[i];
  5229. var probe = mux.demux.probe;
  5230. if (probe(data)) {
  5231. var _remuxer = this.remuxer = new mux.remux(observer, config, typeSupported, this.vendor);
  5232. demuxer = new mux.demux(observer, _remuxer, config, typeSupported);
  5233. this.probe = probe;
  5234. break;
  5235. }
  5236. }
  5237. if (!demuxer) {
  5238. observer.trigger(events["a" /* default */].ERROR, {
  5239. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  5240. details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR,
  5241. fatal: true,
  5242. reason: 'no demux matching with content found'
  5243. });
  5244. return;
  5245. }
  5246. this.demuxer = demuxer;
  5247. }
  5248. var remuxer = this.remuxer;
  5249. if (discontinuity || trackSwitch) {
  5250. demuxer.resetInitSegment(initSegment, audioCodec, videoCodec, duration);
  5251. remuxer.resetInitSegment();
  5252. }
  5253. if (discontinuity) {
  5254. demuxer.resetTimeStamp(defaultInitPTS);
  5255. remuxer.resetTimeStamp(defaultInitPTS);
  5256. }
  5257. if (typeof demuxer.setDecryptData === 'function') {
  5258. demuxer.setDecryptData(decryptdata);
  5259. }
  5260. demuxer.append(data, timeOffset, contiguous, accurateTimeOffset);
  5261. };
  5262. return DemuxerInline;
  5263. }();
  5264. /* harmony default export */
  5265. var demuxer_inline = __webpack_exports__["a"] = (demuxer_inline_DemuxerInline);
  5266. /***/
  5267. }),
  5268. /* 9 */
  5269. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  5270. "use strict";
  5271. Object.defineProperty(__webpack_exports__, "__esModule", {value: true});
  5272. var cues_namespaceObject = {};
  5273. __webpack_require__.d(cues_namespaceObject, "newCue", function () {
  5274. return newCue;
  5275. });
  5276. // EXTERNAL MODULE: ./node_modules/url-toolkit/src/url-toolkit.js
  5277. var url_toolkit = __webpack_require__(3);
  5278. var url_toolkit_default = /*#__PURE__*/__webpack_require__.n(url_toolkit);
  5279. // EXTERNAL MODULE: ./src/events.js
  5280. var events = __webpack_require__(1);
  5281. // EXTERNAL MODULE: ./src/errors.js
  5282. var errors = __webpack_require__(2);
  5283. // EXTERNAL MODULE: ./src/utils/logger.js
  5284. var logger = __webpack_require__(0);
  5285. // CONCATENATED MODULE: ./src/event-handler.js
  5286. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  5287. return typeof obj;
  5288. } : function (obj) {
  5289. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  5290. };
  5291. function _classCallCheck(instance, Constructor) {
  5292. if (!(instance instanceof Constructor)) {
  5293. throw new TypeError("Cannot call a class as a function");
  5294. }
  5295. }
  5296. /*
  5297. *
  5298. * All objects in the event handling chain should inherit from this class
  5299. *
  5300. */
  5301. var FORBIDDEN_EVENT_NAMES = new Set(['hlsEventGeneric', 'hlsHandlerDestroying', 'hlsHandlerDestroyed']);
  5302. var event_handler_EventHandler = function () {
  5303. function EventHandler(hls) {
  5304. _classCallCheck(this, EventHandler);
  5305. this.hls = hls;
  5306. this.onEvent = this.onEvent.bind(this);
  5307. for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  5308. events[_key - 1] = arguments[_key];
  5309. }
  5310. this.handledEvents = events;
  5311. this.useGenericHandler = true;
  5312. this.registerListeners();
  5313. }
  5314. EventHandler.prototype.destroy = function destroy() {
  5315. this.onHandlerDestroying();
  5316. this.unregisterListeners();
  5317. this.onHandlerDestroyed();
  5318. };
  5319. EventHandler.prototype.onHandlerDestroying = function onHandlerDestroying() {
  5320. };
  5321. EventHandler.prototype.onHandlerDestroyed = function onHandlerDestroyed() {
  5322. };
  5323. EventHandler.prototype.isEventHandler = function isEventHandler() {
  5324. return _typeof(this.handledEvents) === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
  5325. };
  5326. EventHandler.prototype.registerListeners = function registerListeners() {
  5327. if (this.isEventHandler()) {
  5328. this.handledEvents.forEach(function (event) {
  5329. if (FORBIDDEN_EVENT_NAMES.has(event)) {
  5330. throw new Error('Forbidden event-name: ' + event);
  5331. }
  5332. this.hls.on(event, this.onEvent);
  5333. }, this);
  5334. }
  5335. };
  5336. EventHandler.prototype.unregisterListeners = function unregisterListeners() {
  5337. if (this.isEventHandler()) {
  5338. this.handledEvents.forEach(function (event) {
  5339. this.hls.off(event, this.onEvent);
  5340. }, this);
  5341. }
  5342. };
  5343. /**
  5344. * arguments: event (string), data (any)
  5345. */
  5346. EventHandler.prototype.onEvent = function onEvent(event, data) {
  5347. this.onEventGeneric(event, data);
  5348. };
  5349. EventHandler.prototype.onEventGeneric = function onEventGeneric(event, data) {
  5350. var eventToFunction = function eventToFunction(event, data) {
  5351. var funcName = 'on' + event.replace('hls', '');
  5352. if (typeof this[funcName] !== 'function') {
  5353. throw new Error('Event ' + event + ' has no generic handler in this ' + this.constructor.name + ' class (tried ' + funcName + ')');
  5354. }
  5355. return this[funcName].bind(this, data);
  5356. };
  5357. try {
  5358. eventToFunction.call(this, event, data).call();
  5359. } catch (err) {
  5360. logger["b" /* logger */].error('An internal error happened while handling event ' + event + '. Error message: "' + err.message + '". Here is a stacktrace:', err);
  5361. this.hls.trigger(events["a" /* default */].ERROR, {
  5362. type: errors["b" /* ErrorTypes */].OTHER_ERROR,
  5363. details: errors["a" /* ErrorDetails */].INTERNAL_EXCEPTION,
  5364. fatal: false,
  5365. event: event,
  5366. err: err
  5367. });
  5368. }
  5369. };
  5370. return EventHandler;
  5371. }();
  5372. /* harmony default export */
  5373. var event_handler = (event_handler_EventHandler);
  5374. // EXTERNAL MODULE: ./src/demux/mp4demuxer.js
  5375. var mp4demuxer = __webpack_require__(7);
  5376. // CONCATENATED MODULE: ./src/loader/level-key.js
  5377. var _createClass = function () {
  5378. function defineProperties(target, props) {
  5379. for (var i = 0; i < props.length; i++) {
  5380. var descriptor = props[i];
  5381. descriptor.enumerable = descriptor.enumerable || false;
  5382. descriptor.configurable = true;
  5383. if ("value" in descriptor) descriptor.writable = true;
  5384. Object.defineProperty(target, descriptor.key, descriptor);
  5385. }
  5386. }
  5387. return function (Constructor, protoProps, staticProps) {
  5388. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  5389. if (staticProps) defineProperties(Constructor, staticProps);
  5390. return Constructor;
  5391. };
  5392. }();
  5393. function level_key__classCallCheck(instance, Constructor) {
  5394. if (!(instance instanceof Constructor)) {
  5395. throw new TypeError("Cannot call a class as a function");
  5396. }
  5397. }
  5398. var level_key_LevelKey = function () {
  5399. function LevelKey() {
  5400. level_key__classCallCheck(this, LevelKey);
  5401. this.method = null;
  5402. this.key = null;
  5403. this.iv = null;
  5404. this._uri = null;
  5405. }
  5406. _createClass(LevelKey, [{
  5407. key: 'uri',
  5408. get: function get() {
  5409. if (!this._uri && this.reluri) {
  5410. this._uri = url_toolkit_default.a.buildAbsoluteURL(this.baseuri, this.reluri, {alwaysNormalize: true});
  5411. }
  5412. return this._uri;
  5413. }
  5414. }]);
  5415. return LevelKey;
  5416. }();
  5417. /* harmony default export */
  5418. var level_key = (level_key_LevelKey);
  5419. // CONCATENATED MODULE: ./src/loader/fragment.js
  5420. var fragment__createClass = function () {
  5421. function defineProperties(target, props) {
  5422. for (var i = 0; i < props.length; i++) {
  5423. var descriptor = props[i];
  5424. descriptor.enumerable = descriptor.enumerable || false;
  5425. descriptor.configurable = true;
  5426. if ("value" in descriptor) descriptor.writable = true;
  5427. Object.defineProperty(target, descriptor.key, descriptor);
  5428. }
  5429. }
  5430. return function (Constructor, protoProps, staticProps) {
  5431. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  5432. if (staticProps) defineProperties(Constructor, staticProps);
  5433. return Constructor;
  5434. };
  5435. }();
  5436. function fragment__classCallCheck(instance, Constructor) {
  5437. if (!(instance instanceof Constructor)) {
  5438. throw new TypeError("Cannot call a class as a function");
  5439. }
  5440. }
  5441. var fragment_Fragment = function () {
  5442. function Fragment() {
  5443. fragment__classCallCheck(this, Fragment);
  5444. this._url = null;
  5445. this._byteRange = null;
  5446. this._decryptdata = null;
  5447. this.tagList = [];
  5448. }
  5449. /**
  5450. * Utility method for parseLevelPlaylist to create an initialization vector for a given segment
  5451. * @returns {Uint8Array}
  5452. */
  5453. Fragment.prototype.createInitializationVector = function createInitializationVector(segmentNumber) {
  5454. var uint8View = new Uint8Array(16);
  5455. for (var i = 12; i < 16; i++) {
  5456. uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff;
  5457. }
  5458. return uint8View;
  5459. };
  5460. /**
  5461. * Utility method for parseLevelPlaylist to get a fragment's decryption data from the currently parsed encryption key data
  5462. * @param levelkey - a playlist's encryption info
  5463. * @param segmentNumber - the fragment's segment number
  5464. * @returns {*} - an object to be applied as a fragment's decryptdata
  5465. */
  5466. Fragment.prototype.fragmentDecryptdataFromLevelkey = function fragmentDecryptdataFromLevelkey(levelkey, segmentNumber) {
  5467. var decryptdata = levelkey;
  5468. if (levelkey && levelkey.method && levelkey.uri && !levelkey.iv) {
  5469. decryptdata = new level_key();
  5470. decryptdata.method = levelkey.method;
  5471. decryptdata.baseuri = levelkey.baseuri;
  5472. decryptdata.reluri = levelkey.reluri;
  5473. decryptdata.iv = this.createInitializationVector(segmentNumber);
  5474. }
  5475. return decryptdata;
  5476. };
  5477. Fragment.prototype.cloneObj = function cloneObj(obj) {
  5478. return JSON.parse(JSON.stringify(obj));
  5479. };
  5480. fragment__createClass(Fragment, [{
  5481. key: 'url',
  5482. get: function get() {
  5483. if (!this._url && this.relurl) {
  5484. this._url = url_toolkit_default.a.buildAbsoluteURL(this.baseurl, this.relurl, {alwaysNormalize: true});
  5485. }
  5486. return this._url;
  5487. },
  5488. set: function set(value) {
  5489. this._url = value;
  5490. }
  5491. }, {
  5492. key: 'programDateTime',
  5493. get: function get() {
  5494. if (!this._programDateTime && this.rawProgramDateTime) {
  5495. this._programDateTime = new Date(Date.parse(this.rawProgramDateTime));
  5496. }
  5497. return this._programDateTime;
  5498. }
  5499. }, {
  5500. key: 'byteRange',
  5501. get: function get() {
  5502. if (!this._byteRange && !this.rawByteRange) {
  5503. return [];
  5504. }
  5505. if (this._byteRange) {
  5506. return this._byteRange;
  5507. }
  5508. var byteRange = [];
  5509. if (this.rawByteRange) {
  5510. var params = this.rawByteRange.split('@', 2);
  5511. if (params.length === 1) {
  5512. var lastByteRangeEndOffset = this.lastByteRangeEndOffset;
  5513. byteRange[0] = lastByteRangeEndOffset ? lastByteRangeEndOffset : 0;
  5514. } else {
  5515. byteRange[0] = parseInt(params[1]);
  5516. }
  5517. byteRange[1] = parseInt(params[0]) + byteRange[0];
  5518. this._byteRange = byteRange;
  5519. }
  5520. return byteRange;
  5521. }
  5522. }, {
  5523. key: 'byteRangeStartOffset',
  5524. get: function get() {
  5525. return this.byteRange[0];
  5526. }
  5527. }, {
  5528. key: 'byteRangeEndOffset',
  5529. get: function get() {
  5530. return this.byteRange[1];
  5531. }
  5532. }, {
  5533. key: 'decryptdata',
  5534. get: function get() {
  5535. if (!this._decryptdata) {
  5536. this._decryptdata = this.fragmentDecryptdataFromLevelkey(this.levelkey, this.sn);
  5537. }
  5538. return this._decryptdata;
  5539. }
  5540. }]);
  5541. return Fragment;
  5542. }();
  5543. /* harmony default export */
  5544. var loader_fragment = (fragment_Fragment);
  5545. // CONCATENATED MODULE: ./src/utils/attr-list.js
  5546. function attr_list__classCallCheck(instance, Constructor) {
  5547. if (!(instance instanceof Constructor)) {
  5548. throw new TypeError("Cannot call a class as a function");
  5549. }
  5550. }
  5551. var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
  5552. var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g;
  5553. // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
  5554. var AttrList = function () {
  5555. function AttrList(attrs) {
  5556. attr_list__classCallCheck(this, AttrList);
  5557. if (typeof attrs === 'string') {
  5558. attrs = AttrList.parseAttrList(attrs);
  5559. }
  5560. for (var attr in attrs) {
  5561. if (attrs.hasOwnProperty(attr)) {
  5562. this[attr] = attrs[attr];
  5563. }
  5564. }
  5565. }
  5566. AttrList.prototype.decimalInteger = function decimalInteger(attrName) {
  5567. var intValue = parseInt(this[attrName], 10);
  5568. if (intValue > Number.MAX_SAFE_INTEGER) {
  5569. return Infinity;
  5570. }
  5571. return intValue;
  5572. };
  5573. AttrList.prototype.hexadecimalInteger = function hexadecimalInteger(attrName) {
  5574. if (this[attrName]) {
  5575. var stringValue = (this[attrName] || '0x').slice(2);
  5576. stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;
  5577. var value = new Uint8Array(stringValue.length / 2);
  5578. for (var i = 0; i < stringValue.length / 2; i++) {
  5579. value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);
  5580. }
  5581. return value;
  5582. } else {
  5583. return null;
  5584. }
  5585. };
  5586. AttrList.prototype.hexadecimalIntegerAsNumber = function hexadecimalIntegerAsNumber(attrName) {
  5587. var intValue = parseInt(this[attrName], 16);
  5588. if (intValue > Number.MAX_SAFE_INTEGER) {
  5589. return Infinity;
  5590. }
  5591. return intValue;
  5592. };
  5593. AttrList.prototype.decimalFloatingPoint = function decimalFloatingPoint(attrName) {
  5594. return parseFloat(this[attrName]);
  5595. };
  5596. AttrList.prototype.enumeratedString = function enumeratedString(attrName) {
  5597. return this[attrName];
  5598. };
  5599. AttrList.prototype.decimalResolution = function decimalResolution(attrName) {
  5600. var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);
  5601. if (res === null) {
  5602. return undefined;
  5603. }
  5604. return {
  5605. width: parseInt(res[1], 10),
  5606. height: parseInt(res[2], 10)
  5607. };
  5608. };
  5609. AttrList.parseAttrList = function parseAttrList(input) {
  5610. var match,
  5611. attrs = {};
  5612. ATTR_LIST_REGEX.lastIndex = 0;
  5613. while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
  5614. var value = match[2],
  5615. quote = '"';
  5616. if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) {
  5617. value = value.slice(1, -1);
  5618. }
  5619. attrs[match[1]] = value;
  5620. }
  5621. return attrs;
  5622. };
  5623. return AttrList;
  5624. }();
  5625. /* harmony default export */
  5626. var attr_list = (AttrList);
  5627. // CONCATENATED MODULE: ./src/utils/codecs.js
  5628. // from http://mp4ra.org/codecs.html
  5629. var sampleEntryCodesISO = {
  5630. audio: {
  5631. 'a3ds': true,
  5632. 'ac-3': true,
  5633. 'ac-4': true,
  5634. 'alac': true,
  5635. 'alaw': true,
  5636. 'dra1': true,
  5637. 'dts+': true,
  5638. 'dts-': true,
  5639. 'dtsc': true,
  5640. 'dtse': true,
  5641. 'dtsh': true,
  5642. 'ec-3': true,
  5643. 'enca': true,
  5644. 'g719': true,
  5645. 'g726': true,
  5646. 'm4ae': true,
  5647. 'mha1': true,
  5648. 'mha2': true,
  5649. 'mhm1': true,
  5650. 'mhm2': true,
  5651. 'mlpa': true,
  5652. 'mp4a': true,
  5653. 'raw ': true,
  5654. 'Opus': true,
  5655. 'samr': true,
  5656. 'sawb': true,
  5657. 'sawp': true,
  5658. 'sevc': true,
  5659. 'sqcp': true,
  5660. 'ssmv': true,
  5661. 'twos': true,
  5662. 'ulaw': true
  5663. },
  5664. video: {
  5665. 'avc1': true,
  5666. 'avc2': true,
  5667. 'avc3': true,
  5668. 'avc4': true,
  5669. 'avcp': true,
  5670. 'drac': true,
  5671. 'dvav': true,
  5672. 'dvhe': true,
  5673. 'encv': true,
  5674. 'hev1': true,
  5675. 'hvc1': true,
  5676. 'mjp2': true,
  5677. 'mp4v': true,
  5678. 'mvc1': true,
  5679. 'mvc2': true,
  5680. 'mvc3': true,
  5681. 'mvc4': true,
  5682. 'resv': true,
  5683. 'rv60': true,
  5684. 's263': true,
  5685. 'svc1': true,
  5686. 'svc2': true,
  5687. 'vc-1': true,
  5688. 'vp08': true,
  5689. 'vp09': true
  5690. }
  5691. };
  5692. function isCodecType(codec, type) {
  5693. var typeCodes = sampleEntryCodesISO[type];
  5694. return !!typeCodes && typeCodes[codec.slice(0, 4)] === true;
  5695. }
  5696. function isCodecSupportedInMp4(codec, type) {
  5697. return MediaSource.isTypeSupported((type || 'video') + '/mp4;codecs="' + codec + '"');
  5698. }
  5699. // CONCATENATED MODULE: ./src/loader/m3u8-parser.js
  5700. function m3u8_parser__classCallCheck(instance, Constructor) {
  5701. if (!(instance instanceof Constructor)) {
  5702. throw new TypeError("Cannot call a class as a function");
  5703. }
  5704. }
  5705. /**
  5706. * M3U8 parser
  5707. * @module
  5708. */
  5709. // https://regex101.com is your friend
  5710. var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g;
  5711. var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
  5712. var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, // duration (#EXTINF:<duration>,<title>), group 1 => duration, group 2 => title
  5713. /|(?!#)(\S+)/.source, // segment URI, group 3 => the URI (note newline is not eaten)
  5714. /|#EXT-X-BYTERANGE:*(.+)/.source, // next segment's byterange, group 4 => range spec (x@y)
  5715. /|#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, // next segment's program date/time group 5 => the datetime spec
  5716. /|#.*/.source // All other non-segment oriented tags will match with all groups empty
  5717. ].join(''), 'g');
  5718. var LEVEL_PLAYLIST_REGEX_SLOW = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:(#)(.*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/;
  5719. var m3u8_parser_M3U8Parser = function () {
  5720. function M3U8Parser() {
  5721. m3u8_parser__classCallCheck(this, M3U8Parser);
  5722. }
  5723. M3U8Parser.findGroup = function findGroup(groups, mediaGroupId) {
  5724. if (!groups) {
  5725. return null;
  5726. }
  5727. var matchingGroup = null;
  5728. for (var i = 0; i < groups.length; i++) {
  5729. var group = groups[i];
  5730. if (group.id === mediaGroupId) {
  5731. matchingGroup = group;
  5732. }
  5733. }
  5734. return matchingGroup;
  5735. };
  5736. M3U8Parser.convertAVC1ToAVCOTI = function convertAVC1ToAVCOTI(codec) {
  5737. var result,
  5738. avcdata = codec.split('.');
  5739. if (avcdata.length > 2) {
  5740. result = avcdata.shift() + '.';
  5741. result += parseInt(avcdata.shift()).toString(16);
  5742. result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4);
  5743. } else {
  5744. result = codec;
  5745. }
  5746. return result;
  5747. };
  5748. M3U8Parser.resolve = function resolve(url, baseUrl) {
  5749. return url_toolkit_default.a.buildAbsoluteURL(baseUrl, url, {alwaysNormalize: true});
  5750. };
  5751. M3U8Parser.parseMasterPlaylist = function parseMasterPlaylist(string, baseurl) {
  5752. var levels = [],
  5753. result = void 0;
  5754. MASTER_PLAYLIST_REGEX.lastIndex = 0;
  5755. function setCodecs(codecs, level) {
  5756. ['video', 'audio'].forEach(function (type) {
  5757. var filtered = codecs.filter(function (codec) {
  5758. return isCodecType(codec, type);
  5759. });
  5760. if (filtered.length) {
  5761. var preferred = filtered.filter(function (codec) {
  5762. return codec.lastIndexOf('avc1', 0) === 0 || codec.lastIndexOf('mp4a', 0) === 0;
  5763. });
  5764. level[type + 'Codec'] = preferred.length > 0 ? preferred[0] : filtered[0];
  5765. // remove from list
  5766. codecs = codecs.filter(function (codec) {
  5767. return filtered.indexOf(codec) === -1;
  5768. });
  5769. }
  5770. });
  5771. level.unknownCodecs = codecs;
  5772. }
  5773. while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) {
  5774. var level = {};
  5775. var attrs = level.attrs = new attr_list(result[1]);
  5776. level.url = M3U8Parser.resolve(result[2], baseurl);
  5777. var resolution = attrs.decimalResolution('RESOLUTION');
  5778. if (resolution) {
  5779. level.width = resolution.width;
  5780. level.height = resolution.height;
  5781. }
  5782. level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH');
  5783. level.name = attrs.NAME;
  5784. setCodecs([].concat((attrs.CODECS || '').split(/[ ,]+/)), level);
  5785. if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) {
  5786. level.videoCodec = M3U8Parser.convertAVC1ToAVCOTI(level.videoCodec);
  5787. }
  5788. levels.push(level);
  5789. }
  5790. return levels;
  5791. };
  5792. M3U8Parser.parseMasterPlaylistMedia = function parseMasterPlaylistMedia(string, baseurl, type) {
  5793. var audioGroups = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
  5794. var result = void 0;
  5795. var medias = [];
  5796. var id = 0;
  5797. MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0;
  5798. while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) {
  5799. var media = {};
  5800. var attrs = new attr_list(result[1]);
  5801. if (attrs.TYPE === type) {
  5802. media.groupId = attrs['GROUP-ID'];
  5803. media.name = attrs.NAME;
  5804. media.type = type;
  5805. media.default = attrs.DEFAULT === 'YES';
  5806. media.autoselect = attrs.AUTOSELECT === 'YES';
  5807. media.forced = attrs.FORCED === 'YES';
  5808. if (attrs.URI) {
  5809. media.url = M3U8Parser.resolve(attrs.URI, baseurl);
  5810. }
  5811. media.lang = attrs.LANGUAGE;
  5812. if (!media.name) {
  5813. media.name = media.lang;
  5814. }
  5815. if (audioGroups.length) {
  5816. var groupCodec = M3U8Parser.findGroup(audioGroups, media.groupId);
  5817. media.audioCodec = groupCodec ? groupCodec.codec : audioGroups[0].codec;
  5818. }
  5819. media.id = id++;
  5820. medias.push(media);
  5821. }
  5822. }
  5823. return medias;
  5824. };
  5825. M3U8Parser.parseLevelPlaylist = function parseLevelPlaylist(string, baseurl, id, type) {
  5826. var currentSN = 0,
  5827. totalduration = 0,
  5828. level = {type: null, version: null, url: baseurl, fragments: [], live: true, startSN: 0},
  5829. levelkey = new level_key(),
  5830. cc = 0,
  5831. prevFrag = null,
  5832. frag = new loader_fragment(),
  5833. result,
  5834. i;
  5835. LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0;
  5836. while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {
  5837. var duration = result[1];
  5838. if (duration) {
  5839. // INF
  5840. frag.duration = parseFloat(duration);
  5841. // avoid sliced strings https://github.com/video-dev/hls.js/issues/939
  5842. var title = (' ' + result[2]).slice(1);
  5843. frag.title = title ? title : null;
  5844. frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]);
  5845. } else if (result[3]) {
  5846. // url
  5847. if (!isNaN(frag.duration)) {
  5848. var sn = currentSN++;
  5849. frag.type = type;
  5850. frag.start = totalduration;
  5851. frag.levelkey = levelkey;
  5852. frag.sn = sn;
  5853. frag.level = id;
  5854. frag.cc = cc;
  5855. frag.baseurl = baseurl;
  5856. // avoid sliced strings https://github.com/video-dev/hls.js/issues/939
  5857. frag.relurl = (' ' + result[3]).slice(1);
  5858. level.fragments.push(frag);
  5859. prevFrag = frag;
  5860. totalduration += frag.duration;
  5861. frag = new loader_fragment();
  5862. }
  5863. } else if (result[4]) {
  5864. // X-BYTERANGE
  5865. frag.rawByteRange = (' ' + result[4]).slice(1);
  5866. if (prevFrag) {
  5867. var lastByteRangeEndOffset = prevFrag.byteRangeEndOffset;
  5868. if (lastByteRangeEndOffset) {
  5869. frag.lastByteRangeEndOffset = lastByteRangeEndOffset;
  5870. }
  5871. }
  5872. } else if (result[5]) {
  5873. // PROGRAM-DATE-TIME
  5874. // avoid sliced strings https://github.com/video-dev/hls.js/issues/939
  5875. frag.rawProgramDateTime = (' ' + result[5]).slice(1);
  5876. frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]);
  5877. if (level.programDateTime === undefined) {
  5878. level.programDateTime = new Date(new Date(Date.parse(result[5])) - 1000 * totalduration);
  5879. }
  5880. } else {
  5881. result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW);
  5882. for (i = 1; i < result.length; i++) {
  5883. if (result[i] !== undefined) {
  5884. break;
  5885. }
  5886. }
  5887. // avoid sliced strings https://github.com/video-dev/hls.js/issues/939
  5888. var value1 = (' ' + result[i + 1]).slice(1);
  5889. var value2 = (' ' + result[i + 2]).slice(1);
  5890. switch (result[i]) {
  5891. case '#':
  5892. frag.tagList.push(value2 ? [value1, value2] : [value1]);
  5893. break;
  5894. case 'PLAYLIST-TYPE':
  5895. level.type = value1.toUpperCase();
  5896. break;
  5897. case 'MEDIA-SEQUENCE':
  5898. currentSN = level.startSN = parseInt(value1);
  5899. break;
  5900. case 'TARGETDURATION':
  5901. level.targetduration = parseFloat(value1);
  5902. break;
  5903. case 'VERSION':
  5904. level.version = parseInt(value1);
  5905. break;
  5906. case 'EXTM3U':
  5907. break;
  5908. case 'ENDLIST':
  5909. level.live = false;
  5910. break;
  5911. case 'DIS':
  5912. cc++;
  5913. frag.tagList.push(['DIS']);
  5914. break;
  5915. case 'DISCONTINUITY-SEQ':
  5916. cc = parseInt(value1);
  5917. break;
  5918. case 'KEY':
  5919. // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4
  5920. var decryptparams = value1;
  5921. var keyAttrs = new attr_list(decryptparams);
  5922. var decryptmethod = keyAttrs.enumeratedString('METHOD'),
  5923. decrypturi = keyAttrs.URI,
  5924. decryptiv = keyAttrs.hexadecimalInteger('IV');
  5925. if (decryptmethod) {
  5926. levelkey = new level_key();
  5927. if (decrypturi && ['AES-128', 'SAMPLE-AES', 'SAMPLE-AES-CENC'].indexOf(decryptmethod) >= 0) {
  5928. levelkey.method = decryptmethod;
  5929. // URI to get the key
  5930. levelkey.baseuri = baseurl;
  5931. levelkey.reluri = decrypturi;
  5932. levelkey.key = null;
  5933. // Initialization Vector (IV)
  5934. levelkey.iv = decryptiv;
  5935. }
  5936. }
  5937. break;
  5938. case 'START':
  5939. var startParams = value1;
  5940. var startAttrs = new attr_list(startParams);
  5941. var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
  5942. //TIME-OFFSET can be 0
  5943. if (!isNaN(startTimeOffset)) {
  5944. level.startTimeOffset = startTimeOffset;
  5945. }
  5946. break;
  5947. case 'MAP':
  5948. var mapAttrs = new attr_list(value1);
  5949. frag.relurl = mapAttrs.URI;
  5950. frag.rawByteRange = mapAttrs.BYTERANGE;
  5951. frag.baseurl = baseurl;
  5952. frag.level = id;
  5953. frag.type = type;
  5954. frag.sn = 'initSegment';
  5955. level.initSegment = frag;
  5956. frag = new loader_fragment();
  5957. break;
  5958. default:
  5959. logger["b" /* logger */].warn('line parsed but not handled: ' + result);
  5960. break;
  5961. }
  5962. }
  5963. }
  5964. frag = prevFrag;
  5965. //logger.log('found ' + level.fragments.length + ' fragments');
  5966. if (frag && !frag.relurl) {
  5967. level.fragments.pop();
  5968. totalduration -= frag.duration;
  5969. }
  5970. level.totalduration = totalduration;
  5971. level.averagetargetduration = totalduration / level.fragments.length;
  5972. level.endSN = currentSN - 1;
  5973. level.startCC = level.fragments[0] ? level.fragments[0].cc : 0;
  5974. level.endCC = cc;
  5975. if (!level.initSegment && level.fragments.length) {
  5976. // this is a bit lurky but HLS really has no other way to tell us
  5977. // if the fragments are TS or MP4, except if we download them :/
  5978. // but this is to be able to handle SIDX.
  5979. // FIXME: replace string test by a regex that matches
  5980. // also `m4s` `m4a` `m4v` and other popular extensions
  5981. if (level.fragments.every(function (frag) {
  5982. return frag.relurl.endsWith('.mp4');
  5983. })) {
  5984. logger["b" /* logger */].warn('MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX');
  5985. frag = new loader_fragment();
  5986. frag.relurl = level.fragments[0].relurl;
  5987. frag.baseurl = baseurl;
  5988. frag.level = id;
  5989. frag.type = type;
  5990. frag.sn = 'initSegment';
  5991. level.initSegment = frag;
  5992. level.needSidxRanges = true;
  5993. }
  5994. }
  5995. return level;
  5996. };
  5997. return M3U8Parser;
  5998. }();
  5999. /* harmony default export */
  6000. var m3u8_parser = (m3u8_parser_M3U8Parser);
  6001. // CONCATENATED MODULE: ./src/loader/playlist-loader.js
  6002. var playlist_loader__createClass = function () {
  6003. function defineProperties(target, props) {
  6004. for (var i = 0; i < props.length; i++) {
  6005. var descriptor = props[i];
  6006. descriptor.enumerable = descriptor.enumerable || false;
  6007. descriptor.configurable = true;
  6008. if ("value" in descriptor) descriptor.writable = true;
  6009. Object.defineProperty(target, descriptor.key, descriptor);
  6010. }
  6011. }
  6012. return function (Constructor, protoProps, staticProps) {
  6013. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  6014. if (staticProps) defineProperties(Constructor, staticProps);
  6015. return Constructor;
  6016. };
  6017. }();
  6018. function playlist_loader__classCallCheck(instance, Constructor) {
  6019. if (!(instance instanceof Constructor)) {
  6020. throw new TypeError("Cannot call a class as a function");
  6021. }
  6022. }
  6023. function _possibleConstructorReturn(self, call) {
  6024. if (!self) {
  6025. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  6026. }
  6027. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  6028. }
  6029. function _inherits(subClass, superClass) {
  6030. if (typeof superClass !== "function" && superClass !== null) {
  6031. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  6032. }
  6033. subClass.prototype = Object.create(superClass && superClass.prototype, {
  6034. constructor: {
  6035. value: subClass,
  6036. enumerable: false,
  6037. writable: true,
  6038. configurable: true
  6039. }
  6040. });
  6041. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  6042. }
  6043. /**
  6044. * PlaylistLoader - delegate for media manifest/playlist loading tasks. Takes care of parsing media to internal data-models.
  6045. *
  6046. * Once loaded, dispatches events with parsed data-models of manifest/levels/audio/subtitle tracks.
  6047. *
  6048. * Uses loader(s) set in config to do actual internal loading of resource tasks.
  6049. *
  6050. * @module
  6051. *
  6052. */
  6053. /**
  6054. * `type` property values for this loaders' context object
  6055. * @enum
  6056. *
  6057. */
  6058. var ContextType = {
  6059. MANIFEST: 'manifest',
  6060. LEVEL: 'level',
  6061. AUDIO_TRACK: 'audioTrack',
  6062. SUBTITLE_TRACK: 'subtitleTrack'
  6063. };
  6064. /**
  6065. * @constructor
  6066. */
  6067. var playlist_loader_PlaylistLoader = function (_EventHandler) {
  6068. _inherits(PlaylistLoader, _EventHandler);
  6069. /**
  6070. * @constructs
  6071. * @param {Hls} hls
  6072. */
  6073. function PlaylistLoader(hls) {
  6074. playlist_loader__classCallCheck(this, PlaylistLoader);
  6075. var _this = _possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MANIFEST_LOADING, events["a" /* default */].LEVEL_LOADING, events["a" /* default */].AUDIO_TRACK_LOADING, events["a" /* default */].SUBTITLE_TRACK_LOADING));
  6076. _this.loaders = {};
  6077. return _this;
  6078. }
  6079. /**
  6080. * @param {ContextType} type
  6081. * @returns {boolean}
  6082. */
  6083. PlaylistLoader.canHaveQualityLevels = function canHaveQualityLevels(type) {
  6084. return type !== ContextType.AUDIO_TRACK && type !== ContextType.SUBTITLE_TRACK;
  6085. };
  6086. PlaylistLoader.mapContextToLevelType = function mapContextToLevelType(context) {
  6087. var type = context.type;
  6088. switch (type) {
  6089. case ContextType.AUDIO_TRACK:
  6090. return 'audio';
  6091. case ContextType.SUBTITLE_TRACK:
  6092. return 'subtitle';
  6093. default:
  6094. return 'main';
  6095. }
  6096. };
  6097. PlaylistLoader.getResponseUrl = function getResponseUrl(response, context) {
  6098. var url = response.url;
  6099. // responseURL not supported on some browsers (it is used to detect URL redirection)
  6100. // data-uri mode also not supported (but no need to detect redirection)
  6101. if (url === undefined || url.indexOf('data:') === 0) {
  6102. // fallback to initial URL
  6103. url = context.url;
  6104. }
  6105. return url;
  6106. };
  6107. /**
  6108. * Returns defaults or configured loader-type overloads (pLoader and loader config params)
  6109. * Default loader is XHRLoader (see utils)
  6110. * @param {object} context
  6111. * @returns {XHRLoader} or other compatible configured overload
  6112. */
  6113. PlaylistLoader.prototype.createInternalLoader = function createInternalLoader(context) {
  6114. var config = this.hls.config;
  6115. var PLoader = config.pLoader;
  6116. var Loader = config.loader;
  6117. var InternalLoader = PLoader || Loader;
  6118. var loader = new InternalLoader(config);
  6119. context.loader = Loader;
  6120. this.loaders[context.type] = loader;
  6121. return loader;
  6122. };
  6123. PlaylistLoader.prototype.getInternalLoader = function getInternalLoader(context) {
  6124. return this.loaders[context.type];
  6125. };
  6126. PlaylistLoader.prototype.resetInternalLoader = function resetInternalLoader(contextType) {
  6127. if (this.loaders[contextType]) {
  6128. delete this.loaders[contextType];
  6129. }
  6130. };
  6131. /**
  6132. * Call `destroy` on all internal loader instances mapped (one per context type)
  6133. */
  6134. PlaylistLoader.prototype.destroyInternalLoaders = function destroyInternalLoaders() {
  6135. for (var contextType in this.loaders) {
  6136. var loader = this.loaders[contextType];
  6137. if (loader) {
  6138. loader.destroy();
  6139. }
  6140. this.resetInternalLoader(contextType);
  6141. }
  6142. };
  6143. PlaylistLoader.prototype.destroy = function destroy() {
  6144. this.destroyInternalLoaders();
  6145. _EventHandler.prototype.destroy.call(this);
  6146. };
  6147. PlaylistLoader.prototype.onManifestLoading = function onManifestLoading(data) {
  6148. this.load(data.url, {type: ContextType.MANIFEST});
  6149. };
  6150. PlaylistLoader.prototype.onLevelLoading = function onLevelLoading(data) {
  6151. this.load(data.url, {type: ContextType.LEVEL, level: data.level, id: data.id});
  6152. };
  6153. PlaylistLoader.prototype.onAudioTrackLoading = function onAudioTrackLoading(data) {
  6154. this.load(data.url, {type: ContextType.AUDIO_TRACK, id: data.id});
  6155. };
  6156. PlaylistLoader.prototype.onSubtitleTrackLoading = function onSubtitleTrackLoading(data) {
  6157. this.load(data.url, {type: ContextType.SUBTITLE_TRACK, id: data.id});
  6158. };
  6159. PlaylistLoader.prototype.load = function load(url, context) {
  6160. var config = this.hls.config;
  6161. // Check if a loader for this context already exists
  6162. var loader = this.getInternalLoader(context);
  6163. if (loader) {
  6164. var loaderContext = loader.context;
  6165. if (loaderContext && loaderContext.url === url) {
  6166. // same URL can't overlap
  6167. logger["b" /* logger */].trace('playlist request ongoing');
  6168. return false;
  6169. } else {
  6170. logger["b" /* logger */].warn('aborting previous loader for type: ' + context.type);
  6171. loader.abort();
  6172. }
  6173. }
  6174. var maxRetry = void 0,
  6175. timeout = void 0,
  6176. retryDelay = void 0,
  6177. maxRetryDelay = void 0;
  6178. // apply different configs for retries depending on
  6179. // context (manifest, level, audio/subs playlist)
  6180. switch (context.type) {
  6181. case ContextType.MANIFEST:
  6182. maxRetry = config.manifestLoadingMaxRetry;
  6183. timeout = config.manifestLoadingTimeOut;
  6184. retryDelay = config.manifestLoadingRetryDelay;
  6185. maxRetryDelay = config.manifestLoadingMaxRetryTimeout;
  6186. break;
  6187. case ContextType.LEVEL:
  6188. // Disable internal loader retry logic, since we are managing retries in Level Controller
  6189. maxRetry = 0;
  6190. timeout = config.levelLoadingTimeOut;
  6191. // TODO Introduce retry settings for audio-track and subtitle-track, it should not use level retry config
  6192. break;
  6193. default:
  6194. maxRetry = config.levelLoadingMaxRetry;
  6195. timeout = config.levelLoadingTimeOut;
  6196. retryDelay = config.levelLoadingRetryDelay;
  6197. maxRetryDelay = config.levelLoadingMaxRetryTimeout;
  6198. logger["b" /* logger */].log('Playlist loader for ' + context.type + ' ' + (context.level || context.id));
  6199. break;
  6200. }
  6201. loader = this.createInternalLoader(context);
  6202. context.url = url;
  6203. context.responseType = context.responseType || ''; // FIXME: (should not be necessary to do this)
  6204. var loaderConfig = void 0,
  6205. loaderCallbacks = void 0;
  6206. loaderConfig = {
  6207. timeout: timeout,
  6208. maxRetry: maxRetry,
  6209. retryDelay: retryDelay,
  6210. maxRetryDelay: maxRetryDelay
  6211. };
  6212. loaderCallbacks = {
  6213. onSuccess: this.loadsuccess.bind(this),
  6214. onError: this.loaderror.bind(this),
  6215. onTimeout: this.loadtimeout.bind(this)
  6216. };
  6217. loader.load(context, loaderConfig, loaderCallbacks);
  6218. return true;
  6219. };
  6220. PlaylistLoader.prototype.loadsuccess = function loadsuccess(response, stats, context) {
  6221. var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  6222. if (context.isSidxRequest) {
  6223. this._handleSidxRequest(response, context);
  6224. this._handlePlaylistLoaded(response, stats, context, networkDetails);
  6225. return;
  6226. }
  6227. this.resetInternalLoader(context.type);
  6228. var string = response.data;
  6229. stats.tload = performance.now();
  6230. //stats.mtime = new Date(target.getResponseHeader('Last-Modified'));
  6231. // Validate if it is an M3U8 at all
  6232. if (string.indexOf('#EXTM3U') !== 0) {
  6233. this._handleManifestParsingError(response, context, 'no EXTM3U delimiter', networkDetails);
  6234. return;
  6235. }
  6236. // Check if chunk-list or master
  6237. if (string.indexOf('#EXTINF:') > 0) {
  6238. this._handleTrackOrLevelPlaylist(response, stats, context, networkDetails);
  6239. } else {
  6240. this._handleMasterPlaylist(response, stats, context, networkDetails);
  6241. }
  6242. };
  6243. PlaylistLoader.prototype.loaderror = function loaderror(response, context) {
  6244. var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  6245. this._handleNetworkError(context, networkDetails);
  6246. };
  6247. PlaylistLoader.prototype.loadtimeout = function loadtimeout(stats, context) {
  6248. var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  6249. this._handleNetworkError(context, networkDetails);
  6250. };
  6251. PlaylistLoader.prototype._handleMasterPlaylist = function _handleMasterPlaylist(response, stats, context, networkDetails) {
  6252. var hls = this.hls;
  6253. var string = response.data;
  6254. var url = PlaylistLoader.getResponseUrl(response, context);
  6255. var levels = m3u8_parser.parseMasterPlaylist(string, url);
  6256. if (!levels.length) {
  6257. this._handleManifestParsingError(response, context, 'no level found in manifest', networkDetails);
  6258. return;
  6259. }
  6260. // multi level playlist, parse level info
  6261. var audioGroups = levels.map(function (level) {
  6262. return {
  6263. id: level.attrs.AUDIO,
  6264. codec: level.audioCodec
  6265. };
  6266. });
  6267. var audioTracks = m3u8_parser.parseMasterPlaylistMedia(string, url, 'AUDIO', audioGroups);
  6268. var subtitles = m3u8_parser.parseMasterPlaylistMedia(string, url, 'SUBTITLES');
  6269. if (audioTracks.length) {
  6270. // check if we have found an audio track embedded in main playlist (audio track without URI attribute)
  6271. var embeddedAudioFound = false;
  6272. audioTracks.forEach(function (audioTrack) {
  6273. if (!audioTrack.url) {
  6274. embeddedAudioFound = true;
  6275. }
  6276. });
  6277. // if no embedded audio track defined, but audio codec signaled in quality level,
  6278. // we need to signal this main audio track this could happen with playlists with
  6279. // alt audio rendition in which quality levels (main)
  6280. // contains both audio+video. but with mixed audio track not signaled
  6281. if (embeddedAudioFound === false && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
  6282. logger["b" /* logger */].log('audio codec signaled in quality level, but no embedded audio track signaled, create one');
  6283. audioTracks.unshift({
  6284. type: 'main',
  6285. name: 'main'
  6286. });
  6287. }
  6288. }
  6289. hls.trigger(events["a" /* default */].MANIFEST_LOADED, {
  6290. levels: levels,
  6291. audioTracks: audioTracks,
  6292. subtitles: subtitles,
  6293. url: url,
  6294. stats: stats,
  6295. networkDetails: networkDetails
  6296. });
  6297. };
  6298. PlaylistLoader.prototype._handleTrackOrLevelPlaylist = function _handleTrackOrLevelPlaylist(response, stats, context, networkDetails) {
  6299. var hls = this.hls;
  6300. var id = context.id,
  6301. level = context.level,
  6302. type = context.type;
  6303. var url = PlaylistLoader.getResponseUrl(response, context);
  6304. var levelId = !isNaN(level) ? level : !isNaN(id) ? id : 0; // level -> id -> 0
  6305. var levelType = PlaylistLoader.mapContextToLevelType(context);
  6306. var levelDetails = m3u8_parser.parseLevelPlaylist(response.data, url, levelId, levelType);
  6307. // set stats on level structure
  6308. levelDetails.tload = stats.tload;
  6309. // We have done our first request (Manifest-type) and receive
  6310. // not a master playlist but a chunk-list (track/level)
  6311. // We fire the manifest-loaded event anyway with the parsed level-details
  6312. // by creating a single-level structure for it.
  6313. if (type === ContextType.MANIFEST) {
  6314. var singleLevel = {
  6315. url: url,
  6316. details: levelDetails
  6317. };
  6318. hls.trigger(events["a" /* default */].MANIFEST_LOADED, {
  6319. levels: [singleLevel],
  6320. audioTracks: [],
  6321. url: url,
  6322. stats: stats,
  6323. networkDetails: networkDetails
  6324. });
  6325. }
  6326. // save parsing time
  6327. stats.tparsed = performance.now();
  6328. // in case we need SIDX ranges
  6329. // return early after calling load for
  6330. // the SIDX box.
  6331. if (levelDetails.needSidxRanges) {
  6332. var sidxUrl = levelDetails.initSegment.url;
  6333. this.load(sidxUrl, {
  6334. isSidxRequest: true,
  6335. type: type,
  6336. level: level,
  6337. levelDetails: levelDetails,
  6338. id: id,
  6339. rangeStart: 0,
  6340. rangeEnd: 2048,
  6341. responseType: 'arraybuffer'
  6342. });
  6343. return;
  6344. }
  6345. // extend the context with the new levelDetails property
  6346. context.levelDetails = levelDetails;
  6347. this._handlePlaylistLoaded(response, stats, context, networkDetails);
  6348. };
  6349. PlaylistLoader.prototype._handleSidxRequest = function _handleSidxRequest(response, context) {
  6350. var sidxInfo = mp4demuxer["a" /* default */].parseSegmentIndex(new Uint8Array(response.data));
  6351. sidxInfo.references.forEach(function (segmentRef, index) {
  6352. var segRefInfo = segmentRef.info;
  6353. var frag = context.levelDetails.fragments[index];
  6354. if (frag.byteRange.length === 0) {
  6355. frag.rawByteRange = String(1 + segRefInfo.end - segRefInfo.start) + '@' + String(segRefInfo.start);
  6356. }
  6357. });
  6358. context.levelDetails.initSegment.rawByteRange = String(sidxInfo.moovEndOffset) + '@0';
  6359. };
  6360. PlaylistLoader.prototype._handleManifestParsingError = function _handleManifestParsingError(response, context, reason, networkDetails) {
  6361. this.hls.trigger(events["a" /* default */].ERROR, {
  6362. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6363. details: errors["a" /* ErrorDetails */].MANIFEST_PARSING_ERROR,
  6364. fatal: true,
  6365. url: response.url,
  6366. reason: reason,
  6367. networkDetails: networkDetails
  6368. });
  6369. };
  6370. PlaylistLoader.prototype._handleNetworkError = function _handleNetworkError(context, networkDetails) {
  6371. var details = void 0;
  6372. var fatal = void 0;
  6373. var loader = context.loader;
  6374. switch (context.type) {
  6375. case ContextType.MANIFEST:
  6376. details = errors["a" /* ErrorDetails */].MANIFEST_LOAD_TIMEOUT;
  6377. fatal = true;
  6378. break;
  6379. case ContextType.LEVEL:
  6380. details = errors["a" /* ErrorDetails */].LEVEL_LOAD_TIMEOUT;
  6381. fatal = false;
  6382. break;
  6383. case ContextType.AUDIO_TRACK:
  6384. details = errors["a" /* ErrorDetails */].AUDIO_TRACK_LOAD_TIMEOUT;
  6385. fatal = false;
  6386. break;
  6387. default:
  6388. // details = ...?
  6389. fatal = false;
  6390. }
  6391. if (loader) {
  6392. loader.abort();
  6393. this.resetInternalLoader(context.type);
  6394. }
  6395. this.hls.trigger(events["a" /* default */].ERROR, {
  6396. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6397. details: details,
  6398. fatal: fatal,
  6399. url: loader.url,
  6400. loader: loader,
  6401. context: context,
  6402. networkDetails: networkDetails
  6403. });
  6404. };
  6405. PlaylistLoader.prototype._handlePlaylistLoaded = function _handlePlaylistLoaded(response, stats, context, networkDetails) {
  6406. var type = context.type,
  6407. level = context.level,
  6408. id = context.id,
  6409. levelDetails = context.levelDetails;
  6410. if (!levelDetails.targetduration) {
  6411. this._handleManifestParsingError(response, context, 'invalid target duration', networkDetails);
  6412. return;
  6413. }
  6414. var canHaveLevels = PlaylistLoader.canHaveQualityLevels(context.type);
  6415. if (canHaveLevels) {
  6416. this.hls.trigger(events["a" /* default */].LEVEL_LOADED, {
  6417. details: levelDetails,
  6418. level: level || 0,
  6419. id: id || 0,
  6420. stats: stats,
  6421. networkDetails: networkDetails
  6422. });
  6423. } else {
  6424. switch (type) {
  6425. case ContextType.AUDIO_TRACK:
  6426. this.hls.trigger(events["a" /* default */].AUDIO_TRACK_LOADED, {
  6427. details: levelDetails,
  6428. id: id,
  6429. stats: stats,
  6430. networkDetails: networkDetails
  6431. });
  6432. break;
  6433. case ContextType.SUBTITLE_TRACK:
  6434. this.hls.trigger(events["a" /* default */].SUBTITLE_TRACK_LOADED, {
  6435. details: levelDetails,
  6436. id: id,
  6437. stats: stats,
  6438. networkDetails: networkDetails
  6439. });
  6440. break;
  6441. }
  6442. }
  6443. };
  6444. playlist_loader__createClass(PlaylistLoader, null, [{
  6445. key: 'ContextType',
  6446. get: function get() {
  6447. return ContextType;
  6448. }
  6449. }]);
  6450. return PlaylistLoader;
  6451. }(event_handler);
  6452. /* harmony default export */
  6453. var playlist_loader = (playlist_loader_PlaylistLoader);
  6454. // CONCATENATED MODULE: ./src/loader/fragment-loader.js
  6455. function fragment_loader__classCallCheck(instance, Constructor) {
  6456. if (!(instance instanceof Constructor)) {
  6457. throw new TypeError("Cannot call a class as a function");
  6458. }
  6459. }
  6460. function fragment_loader__possibleConstructorReturn(self, call) {
  6461. if (!self) {
  6462. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  6463. }
  6464. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  6465. }
  6466. function fragment_loader__inherits(subClass, superClass) {
  6467. if (typeof superClass !== "function" && superClass !== null) {
  6468. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  6469. }
  6470. subClass.prototype = Object.create(superClass && superClass.prototype, {
  6471. constructor: {
  6472. value: subClass,
  6473. enumerable: false,
  6474. writable: true,
  6475. configurable: true
  6476. }
  6477. });
  6478. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  6479. }
  6480. /*
  6481. * Fragment Loader
  6482. */
  6483. var fragment_loader_FragmentLoader = function (_EventHandler) {
  6484. fragment_loader__inherits(FragmentLoader, _EventHandler);
  6485. function FragmentLoader(hls) {
  6486. fragment_loader__classCallCheck(this, FragmentLoader);
  6487. var _this = fragment_loader__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FRAG_LOADING));
  6488. _this.loaders = {};
  6489. return _this;
  6490. }
  6491. FragmentLoader.prototype.destroy = function destroy() {
  6492. var loaders = this.loaders;
  6493. for (var loaderName in loaders) {
  6494. var loader = loaders[loaderName];
  6495. if (loader) {
  6496. loader.destroy();
  6497. }
  6498. }
  6499. this.loaders = {};
  6500. _EventHandler.prototype.destroy.call(this);
  6501. };
  6502. FragmentLoader.prototype.onFragLoading = function onFragLoading(data) {
  6503. var frag = data.frag,
  6504. type = frag.type,
  6505. loaders = this.loaders,
  6506. config = this.hls.config,
  6507. FragmentILoader = config.fLoader,
  6508. DefaultILoader = config.loader;
  6509. // reset fragment state
  6510. frag.loaded = 0;
  6511. var loader = loaders[type];
  6512. if (loader) {
  6513. logger["b" /* logger */].warn('abort previous fragment loader for type: ' + type);
  6514. loader.abort();
  6515. }
  6516. loader = loaders[type] = frag.loader = !!config.fLoader ? new FragmentILoader(config) : new DefaultILoader(config);
  6517. var loaderContext = void 0,
  6518. loaderConfig = void 0,
  6519. loaderCallbacks = void 0;
  6520. loaderContext = {url: frag.url, frag: frag, responseType: 'arraybuffer', progressData: false};
  6521. var start = frag.byteRangeStartOffset,
  6522. end = frag.byteRangeEndOffset;
  6523. if (!isNaN(start) && !isNaN(end)) {
  6524. loaderContext.rangeStart = start;
  6525. loaderContext.rangeEnd = end;
  6526. }
  6527. loaderConfig = {
  6528. timeout: config.fragLoadingTimeOut,
  6529. maxRetry: 0,
  6530. retryDelay: 0,
  6531. maxRetryDelay: config.fragLoadingMaxRetryTimeout
  6532. };
  6533. loaderCallbacks = {
  6534. onSuccess: this.loadsuccess.bind(this),
  6535. onError: this.loaderror.bind(this),
  6536. onTimeout: this.loadtimeout.bind(this),
  6537. onProgress: this.loadprogress.bind(this)
  6538. };
  6539. loader.load(loaderContext, loaderConfig, loaderCallbacks);
  6540. };
  6541. FragmentLoader.prototype.loadsuccess = function loadsuccess(response, stats, context) {
  6542. var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  6543. var payload = response.data,
  6544. frag = context.frag;
  6545. // detach fragment loader on load success
  6546. frag.loader = undefined;
  6547. this.loaders[frag.type] = undefined;
  6548. this.hls.trigger(events["a" /* default */].FRAG_LOADED, {
  6549. payload: payload,
  6550. frag: frag,
  6551. stats: stats,
  6552. networkDetails: networkDetails
  6553. });
  6554. };
  6555. FragmentLoader.prototype.loaderror = function loaderror(response, context) {
  6556. var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  6557. var loader = context.loader;
  6558. if (loader) {
  6559. loader.abort();
  6560. }
  6561. this.loaders[context.type] = undefined;
  6562. this.hls.trigger(events["a" /* default */].ERROR, {
  6563. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6564. details: errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR,
  6565. fatal: false,
  6566. frag: context.frag,
  6567. response: response,
  6568. networkDetails: networkDetails
  6569. });
  6570. };
  6571. FragmentLoader.prototype.loadtimeout = function loadtimeout(stats, context) {
  6572. var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  6573. var loader = context.loader;
  6574. if (loader) {
  6575. loader.abort();
  6576. }
  6577. this.loaders[context.type] = undefined;
  6578. this.hls.trigger(events["a" /* default */].ERROR, {
  6579. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6580. details: errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT,
  6581. fatal: false,
  6582. frag: context.frag,
  6583. networkDetails: networkDetails
  6584. });
  6585. };
  6586. // data will be used for progressive parsing
  6587. FragmentLoader.prototype.loadprogress = function loadprogress(stats, context, data) {
  6588. var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  6589. // jshint ignore:line
  6590. var frag = context.frag;
  6591. frag.loaded = stats.loaded;
  6592. this.hls.trigger(events["a" /* default */].FRAG_LOAD_PROGRESS, {
  6593. frag: frag,
  6594. stats: stats,
  6595. networkDetails: networkDetails
  6596. });
  6597. };
  6598. return FragmentLoader;
  6599. }(event_handler);
  6600. /* harmony default export */
  6601. var fragment_loader = (fragment_loader_FragmentLoader);
  6602. // CONCATENATED MODULE: ./src/loader/key-loader.js
  6603. function key_loader__classCallCheck(instance, Constructor) {
  6604. if (!(instance instanceof Constructor)) {
  6605. throw new TypeError("Cannot call a class as a function");
  6606. }
  6607. }
  6608. function key_loader__possibleConstructorReturn(self, call) {
  6609. if (!self) {
  6610. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  6611. }
  6612. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  6613. }
  6614. function key_loader__inherits(subClass, superClass) {
  6615. if (typeof superClass !== "function" && superClass !== null) {
  6616. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  6617. }
  6618. subClass.prototype = Object.create(superClass && superClass.prototype, {
  6619. constructor: {
  6620. value: subClass,
  6621. enumerable: false,
  6622. writable: true,
  6623. configurable: true
  6624. }
  6625. });
  6626. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  6627. }
  6628. /*
  6629. * Decrypt key Loader
  6630. */
  6631. var key_loader_KeyLoader = function (_EventHandler) {
  6632. key_loader__inherits(KeyLoader, _EventHandler);
  6633. function KeyLoader(hls) {
  6634. key_loader__classCallCheck(this, KeyLoader);
  6635. var _this = key_loader__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].KEY_LOADING));
  6636. _this.loaders = {};
  6637. _this.decryptkey = null;
  6638. _this.decrypturl = null;
  6639. return _this;
  6640. }
  6641. KeyLoader.prototype.destroy = function destroy() {
  6642. for (var loaderName in this.loaders) {
  6643. var loader = this.loaders[loaderName];
  6644. if (loader) {
  6645. loader.destroy();
  6646. }
  6647. }
  6648. this.loaders = {};
  6649. event_handler.prototype.destroy.call(this);
  6650. };
  6651. KeyLoader.prototype.onKeyLoading = function onKeyLoading(data) {
  6652. var frag = data.frag,
  6653. type = frag.type,
  6654. loader = this.loaders[type],
  6655. decryptdata = frag.decryptdata,
  6656. uri = decryptdata.uri;
  6657. // if uri is different from previous one or if decrypt key not retrieved yet
  6658. if (uri !== this.decrypturl || this.decryptkey === null) {
  6659. var config = this.hls.config;
  6660. if (loader) {
  6661. logger["b" /* logger */].warn('abort previous key loader for type:' + type);
  6662. loader.abort();
  6663. }
  6664. frag.loader = this.loaders[type] = new config.loader(config);
  6665. this.decrypturl = uri;
  6666. this.decryptkey = null;
  6667. var loaderContext = void 0,
  6668. loaderConfig = void 0,
  6669. loaderCallbacks = void 0;
  6670. loaderContext = {url: uri, frag: frag, responseType: 'arraybuffer'};
  6671. loaderConfig = {
  6672. timeout: config.fragLoadingTimeOut,
  6673. maxRetry: config.fragLoadingMaxRetry,
  6674. retryDelay: config.fragLoadingRetryDelay,
  6675. maxRetryDelay: config.fragLoadingMaxRetryTimeout
  6676. };
  6677. loaderCallbacks = {
  6678. onSuccess: this.loadsuccess.bind(this),
  6679. onError: this.loaderror.bind(this),
  6680. onTimeout: this.loadtimeout.bind(this)
  6681. };
  6682. frag.loader.load(loaderContext, loaderConfig, loaderCallbacks);
  6683. } else if (this.decryptkey) {
  6684. // we already loaded this key, return it
  6685. decryptdata.key = this.decryptkey;
  6686. this.hls.trigger(events["a" /* default */].KEY_LOADED, {frag: frag});
  6687. }
  6688. };
  6689. KeyLoader.prototype.loadsuccess = function loadsuccess(response, stats, context) {
  6690. var frag = context.frag;
  6691. this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data);
  6692. // detach fragment loader on load success
  6693. frag.loader = undefined;
  6694. this.loaders[frag.type] = undefined;
  6695. this.hls.trigger(events["a" /* default */].KEY_LOADED, {frag: frag});
  6696. };
  6697. KeyLoader.prototype.loaderror = function loaderror(response, context) {
  6698. var frag = context.frag,
  6699. loader = frag.loader;
  6700. if (loader) {
  6701. loader.abort();
  6702. }
  6703. this.loaders[context.type] = undefined;
  6704. this.hls.trigger(events["a" /* default */].ERROR, {
  6705. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6706. details: errors["a" /* ErrorDetails */].KEY_LOAD_ERROR,
  6707. fatal: false,
  6708. frag: frag,
  6709. response: response
  6710. });
  6711. };
  6712. KeyLoader.prototype.loadtimeout = function loadtimeout(stats, context) {
  6713. var frag = context.frag,
  6714. loader = frag.loader;
  6715. if (loader) {
  6716. loader.abort();
  6717. }
  6718. this.loaders[context.type] = undefined;
  6719. this.hls.trigger(events["a" /* default */].ERROR, {
  6720. type: errors["b" /* ErrorTypes */].NETWORK_ERROR,
  6721. details: errors["a" /* ErrorDetails */].KEY_LOAD_TIMEOUT,
  6722. fatal: false,
  6723. frag: frag
  6724. });
  6725. };
  6726. return KeyLoader;
  6727. }(event_handler);
  6728. /* harmony default export */
  6729. var key_loader = (key_loader_KeyLoader);
  6730. // CONCATENATED MODULE: ./src/utils/binary-search.js
  6731. var BinarySearch = {
  6732. /**
  6733. * Searches for an item in an array which matches a certain condition.
  6734. * This requires the condition to only match one item in the array,
  6735. * and for the array to be ordered.
  6736. *
  6737. * @param {Array} list The array to search.
  6738. * @param {Function} comparisonFunction
  6739. * Called and provided a candidate item as the first argument.
  6740. * Should return:
  6741. * > -1 if the item should be located at a lower index than the provided item.
  6742. * > 1 if the item should be located at a higher index than the provided item.
  6743. * > 0 if the item is the item you're looking for.
  6744. *
  6745. * @return {*} The object if it is found or null otherwise.
  6746. */
  6747. search: function search(list, comparisonFunction) {
  6748. var minIndex = 0;
  6749. var maxIndex = list.length - 1;
  6750. var currentIndex = null;
  6751. var currentElement = null;
  6752. while (minIndex <= maxIndex) {
  6753. currentIndex = (minIndex + maxIndex) / 2 | 0;
  6754. currentElement = list[currentIndex];
  6755. var comparisonResult = comparisonFunction(currentElement);
  6756. if (comparisonResult > 0) {
  6757. minIndex = currentIndex + 1;
  6758. } else if (comparisonResult < 0) {
  6759. maxIndex = currentIndex - 1;
  6760. } else {
  6761. return currentElement;
  6762. }
  6763. }
  6764. return null;
  6765. }
  6766. };
  6767. /* harmony default export */
  6768. var binary_search = (BinarySearch);
  6769. // CONCATENATED MODULE: ./src/helper/buffer-helper.js
  6770. /**
  6771. * Buffer Helper utils, providing methods dealing buffer length retrieval
  6772. */
  6773. var BufferHelper = {
  6774. isBuffered: function isBuffered(media, position) {
  6775. try {
  6776. if (media) {
  6777. var buffered = media.buffered;
  6778. for (var i = 0; i < buffered.length; i++) {
  6779. if (position >= buffered.start(i) && position <= buffered.end(i)) {
  6780. return true;
  6781. }
  6782. }
  6783. }
  6784. } catch (error) {
  6785. // this is to catch
  6786. // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
  6787. // This SourceBuffer has been removed from the parent media source
  6788. }
  6789. return false;
  6790. },
  6791. bufferInfo: function bufferInfo(media, pos, maxHoleDuration) {
  6792. try {
  6793. if (media) {
  6794. var vbuffered = media.buffered,
  6795. buffered = [],
  6796. i;
  6797. for (i = 0; i < vbuffered.length; i++) {
  6798. buffered.push({start: vbuffered.start(i), end: vbuffered.end(i)});
  6799. }
  6800. return this.bufferedInfo(buffered, pos, maxHoleDuration);
  6801. }
  6802. } catch (error) {
  6803. // this is to catch
  6804. // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
  6805. // This SourceBuffer has been removed from the parent media source
  6806. }
  6807. return {len: 0, start: pos, end: pos, nextStart: undefined};
  6808. },
  6809. bufferedInfo: function bufferedInfo(buffered, pos, maxHoleDuration) {
  6810. var buffered2 = [],
  6811. // bufferStart and bufferEnd are buffer boundaries around current video position
  6812. bufferLen,
  6813. bufferStart,
  6814. bufferEnd,
  6815. bufferStartNext,
  6816. i;
  6817. // sort on buffer.start/smaller end (IE does not always return sorted buffered range)
  6818. buffered.sort(function (a, b) {
  6819. var diff = a.start - b.start;
  6820. if (diff) {
  6821. return diff;
  6822. } else {
  6823. return b.end - a.end;
  6824. }
  6825. });
  6826. // there might be some small holes between buffer time range
  6827. // consider that holes smaller than maxHoleDuration are irrelevant and build another
  6828. // buffer time range representations that discards those holes
  6829. for (i = 0; i < buffered.length; i++) {
  6830. var buf2len = buffered2.length;
  6831. if (buf2len) {
  6832. var buf2end = buffered2[buf2len - 1].end;
  6833. // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative)
  6834. if (buffered[i].start - buf2end < maxHoleDuration) {
  6835. // merge overlapping time ranges
  6836. // update lastRange.end only if smaller than item.end
  6837. // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end)
  6838. // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15])
  6839. if (buffered[i].end > buf2end) {
  6840. buffered2[buf2len - 1].end = buffered[i].end;
  6841. }
  6842. } else {
  6843. // big hole
  6844. buffered2.push(buffered[i]);
  6845. }
  6846. } else {
  6847. // first value
  6848. buffered2.push(buffered[i]);
  6849. }
  6850. }
  6851. for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) {
  6852. var start = buffered2[i].start,
  6853. end = buffered2[i].end;
  6854. //logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
  6855. if (pos + maxHoleDuration >= start && pos < end) {
  6856. // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
  6857. bufferStart = start;
  6858. bufferEnd = end;
  6859. bufferLen = bufferEnd - pos;
  6860. } else if (pos + maxHoleDuration < start) {
  6861. bufferStartNext = start;
  6862. break;
  6863. }
  6864. }
  6865. return {len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext};
  6866. }
  6867. };
  6868. /* harmony default export */
  6869. var buffer_helper = (BufferHelper);
  6870. // EXTERNAL MODULE: ./src/demux/demuxer-inline.js + 11 modules
  6871. var demuxer_inline = __webpack_require__(8);
  6872. // EXTERNAL MODULE: ./node_modules/events/events.js
  6873. var events_events = __webpack_require__(6);
  6874. var events_default = /*#__PURE__*/__webpack_require__.n(events_events);
  6875. // EXTERNAL MODULE: ./node_modules/webworkify-webpack/index.js
  6876. var webworkify_webpack = __webpack_require__(10);
  6877. var webworkify_webpack_default = /*#__PURE__*/__webpack_require__.n(webworkify_webpack);
  6878. // CONCATENATED MODULE: ./src/helper/mediasource-helper.js
  6879. /**
  6880. * MediaSource helper
  6881. */
  6882. function getMediaSource() {
  6883. if (typeof window !== 'undefined') {
  6884. return window.MediaSource || window.WebKitMediaSource;
  6885. }
  6886. }
  6887. // CONCATENATED MODULE: ./src/demux/demuxer.js
  6888. function demuxer__classCallCheck(instance, Constructor) {
  6889. if (!(instance instanceof Constructor)) {
  6890. throw new TypeError("Cannot call a class as a function");
  6891. }
  6892. }
  6893. var demuxer_MediaSource = getMediaSource();
  6894. var demuxer_Demuxer = function () {
  6895. function Demuxer(hls, id) {
  6896. demuxer__classCallCheck(this, Demuxer);
  6897. this.hls = hls;
  6898. this.id = id;
  6899. // observer setup
  6900. var observer = this.observer = new events_default.a();
  6901. var config = hls.config;
  6902. observer.trigger = function trigger(event) {
  6903. for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  6904. data[_key - 1] = arguments[_key];
  6905. }
  6906. observer.emit.apply(observer, [event, event].concat(data));
  6907. };
  6908. observer.off = function off(event) {
  6909. for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  6910. data[_key2 - 1] = arguments[_key2];
  6911. }
  6912. observer.removeListener.apply(observer, [event].concat(data));
  6913. };
  6914. var forwardMessage = function (ev, data) {
  6915. data = data || {};
  6916. data.frag = this.frag;
  6917. data.id = this.id;
  6918. hls.trigger(ev, data);
  6919. }.bind(this);
  6920. // forward events to main thread
  6921. observer.on(events["a" /* default */].FRAG_DECRYPTED, forwardMessage);
  6922. observer.on(events["a" /* default */].FRAG_PARSING_INIT_SEGMENT, forwardMessage);
  6923. observer.on(events["a" /* default */].FRAG_PARSING_DATA, forwardMessage);
  6924. observer.on(events["a" /* default */].FRAG_PARSED, forwardMessage);
  6925. observer.on(events["a" /* default */].ERROR, forwardMessage);
  6926. observer.on(events["a" /* default */].FRAG_PARSING_METADATA, forwardMessage);
  6927. observer.on(events["a" /* default */].FRAG_PARSING_USERDATA, forwardMessage);
  6928. observer.on(events["a" /* default */].INIT_PTS_FOUND, forwardMessage);
  6929. var typeSupported = {
  6930. mp4: demuxer_MediaSource.isTypeSupported('video/mp4'),
  6931. mpeg: demuxer_MediaSource.isTypeSupported('audio/mpeg'),
  6932. mp3: demuxer_MediaSource.isTypeSupported('audio/mp4; codecs="mp3"')
  6933. };
  6934. // navigator.vendor is not always available in Web Worker
  6935. // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
  6936. var vendor = navigator.vendor;
  6937. if (config.enableWorker && typeof Worker !== 'undefined') {
  6938. logger["b" /* logger */].log('demuxing in webworker');
  6939. var w = void 0;
  6940. try {
  6941. w = this.w = webworkify_webpack_default()(/*require.resolve*/(11));
  6942. this.onwmsg = this.onWorkerMessage.bind(this);
  6943. w.addEventListener('message', this.onwmsg);
  6944. w.onerror = function (event) {
  6945. hls.trigger(events["a" /* default */].ERROR, {
  6946. type: errors["b" /* ErrorTypes */].OTHER_ERROR,
  6947. details: errors["a" /* ErrorDetails */].INTERNAL_EXCEPTION,
  6948. fatal: true,
  6949. event: 'demuxerWorker',
  6950. err: {message: event.message + ' (' + event.filename + ':' + event.lineno + ')'}
  6951. });
  6952. };
  6953. w.postMessage({
  6954. cmd: 'init',
  6955. typeSupported: typeSupported,
  6956. vendor: vendor,
  6957. id: id,
  6958. config: JSON.stringify(config)
  6959. });
  6960. } catch (err) {
  6961. logger["b" /* logger */].error('error while initializing DemuxerWorker, fallback on DemuxerInline');
  6962. if (w) {
  6963. // revoke the Object URL that was used to create demuxer worker, so as not to leak it
  6964. URL.revokeObjectURL(w.objectURL);
  6965. }
  6966. this.demuxer = new demuxer_inline["a" /* default */](observer, typeSupported, config, vendor);
  6967. this.w = undefined;
  6968. }
  6969. } else {
  6970. this.demuxer = new demuxer_inline["a" /* default */](observer, typeSupported, config, vendor);
  6971. }
  6972. }
  6973. Demuxer.prototype.destroy = function destroy() {
  6974. var w = this.w;
  6975. if (w) {
  6976. w.removeEventListener('message', this.onwmsg);
  6977. w.terminate();
  6978. this.w = null;
  6979. } else {
  6980. var demuxer = this.demuxer;
  6981. if (demuxer) {
  6982. demuxer.destroy();
  6983. this.demuxer = null;
  6984. }
  6985. }
  6986. var observer = this.observer;
  6987. if (observer) {
  6988. observer.removeAllListeners();
  6989. this.observer = null;
  6990. }
  6991. };
  6992. Demuxer.prototype.push = function push(data, initSegment, audioCodec, videoCodec, frag, duration, accurateTimeOffset, defaultInitPTS) {
  6993. var w = this.w;
  6994. var timeOffset = !isNaN(frag.startDTS) ? frag.startDTS : frag.start;
  6995. var decryptdata = frag.decryptdata;
  6996. var lastFrag = this.frag;
  6997. var discontinuity = !(lastFrag && frag.cc === lastFrag.cc);
  6998. var trackSwitch = !(lastFrag && frag.level === lastFrag.level);
  6999. var nextSN = lastFrag && frag.sn === lastFrag.sn + 1;
  7000. var contiguous = !trackSwitch && nextSN;
  7001. if (discontinuity) {
  7002. logger["b" /* logger */].log(this.id + ':discontinuity detected');
  7003. }
  7004. if (trackSwitch) {
  7005. logger["b" /* logger */].log(this.id + ':switch detected');
  7006. }
  7007. this.frag = frag;
  7008. if (w) {
  7009. // post fragment payload as transferable objects for ArrayBuffer (no copy)
  7010. w.postMessage({
  7011. cmd: 'demux',
  7012. data: data,
  7013. decryptdata: decryptdata,
  7014. initSegment: initSegment,
  7015. audioCodec: audioCodec,
  7016. videoCodec: videoCodec,
  7017. timeOffset: timeOffset,
  7018. discontinuity: discontinuity,
  7019. trackSwitch: trackSwitch,
  7020. contiguous: contiguous,
  7021. duration: duration,
  7022. accurateTimeOffset: accurateTimeOffset,
  7023. defaultInitPTS: defaultInitPTS
  7024. }, data instanceof ArrayBuffer ? [data] : []);
  7025. } else {
  7026. var demuxer = this.demuxer;
  7027. if (demuxer) {
  7028. demuxer.push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
  7029. }
  7030. }
  7031. };
  7032. Demuxer.prototype.onWorkerMessage = function onWorkerMessage(ev) {
  7033. var data = ev.data,
  7034. hls = this.hls;
  7035. switch (data.event) {
  7036. case 'init':
  7037. // revoke the Object URL that was used to create demuxer worker, so as not to leak it
  7038. URL.revokeObjectURL(this.w.objectURL);
  7039. break;
  7040. // special case for FRAG_PARSING_DATA: data1 and data2 are transferable objects
  7041. case events["a" /* default */].FRAG_PARSING_DATA:
  7042. data.data.data1 = new Uint8Array(data.data1);
  7043. if (data.data2) {
  7044. data.data.data2 = new Uint8Array(data.data2);
  7045. }
  7046. /* falls through */
  7047. default:
  7048. data.data = data.data || {};
  7049. data.data.frag = this.frag;
  7050. data.data.id = this.id;
  7051. hls.trigger(data.event, data.data);
  7052. break;
  7053. }
  7054. };
  7055. return Demuxer;
  7056. }();
  7057. /* harmony default export */
  7058. var demux_demuxer = (demuxer_Demuxer);
  7059. // CONCATENATED MODULE: ./src/helper/level-helper.js
  7060. /**
  7061. * Level Helper class, providing methods dealing with playlist sliding and drift
  7062. */
  7063. function updatePTS(fragments, fromIdx, toIdx) {
  7064. var fragFrom = fragments[fromIdx],
  7065. fragTo = fragments[toIdx],
  7066. fragToPTS = fragTo.startPTS;
  7067. // if we know startPTS[toIdx]
  7068. if (!isNaN(fragToPTS)) {
  7069. // update fragment duration.
  7070. // it helps to fix drifts between playlist reported duration and fragment real duration
  7071. if (toIdx > fromIdx) {
  7072. fragFrom.duration = fragToPTS - fragFrom.start;
  7073. if (fragFrom.duration < 0) {
  7074. logger["b" /* logger */].warn('negative duration computed for frag ' + fragFrom.sn + ',level ' + fragFrom.level + ', there should be some duration drift between playlist and fragment!');
  7075. }
  7076. } else {
  7077. fragTo.duration = fragFrom.start - fragToPTS;
  7078. if (fragTo.duration < 0) {
  7079. logger["b" /* logger */].warn('negative duration computed for frag ' + fragTo.sn + ',level ' + fragTo.level + ', there should be some duration drift between playlist and fragment!');
  7080. }
  7081. }
  7082. } else {
  7083. // we dont know startPTS[toIdx]
  7084. if (toIdx > fromIdx) {
  7085. fragTo.start = fragFrom.start + fragFrom.duration;
  7086. } else {
  7087. fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0);
  7088. }
  7089. }
  7090. }
  7091. function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) {
  7092. // update frag PTS/DTS
  7093. var maxStartPTS = startPTS;
  7094. if (!isNaN(frag.startPTS)) {
  7095. // delta PTS between audio and video
  7096. var deltaPTS = Math.abs(frag.startPTS - startPTS);
  7097. if (isNaN(frag.deltaPTS)) {
  7098. frag.deltaPTS = deltaPTS;
  7099. } else {
  7100. frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS);
  7101. }
  7102. maxStartPTS = Math.max(startPTS, frag.startPTS);
  7103. startPTS = Math.min(startPTS, frag.startPTS);
  7104. endPTS = Math.max(endPTS, frag.endPTS);
  7105. startDTS = Math.min(startDTS, frag.startDTS);
  7106. endDTS = Math.max(endDTS, frag.endDTS);
  7107. }
  7108. var drift = startPTS - frag.start;
  7109. frag.start = frag.startPTS = startPTS;
  7110. frag.maxStartPTS = maxStartPTS;
  7111. frag.endPTS = endPTS;
  7112. frag.startDTS = startDTS;
  7113. frag.endDTS = endDTS;
  7114. frag.duration = endPTS - startPTS;
  7115. var sn = frag.sn;
  7116. // exit if sn out of range
  7117. if (!details || sn < details.startSN || sn > details.endSN) {
  7118. return 0;
  7119. }
  7120. var fragIdx, fragments, i;
  7121. fragIdx = sn - details.startSN;
  7122. fragments = details.fragments;
  7123. // update frag reference in fragments array
  7124. // rationale is that fragments array might not contain this frag object.
  7125. // this will happpen if playlist has been refreshed between frag loading and call to updateFragPTSDTS()
  7126. // if we don't update frag, we won't be able to propagate PTS info on the playlist
  7127. // resulting in invalid sliding computation
  7128. fragments[fragIdx] = frag;
  7129. // adjust fragment PTS/duration from seqnum-1 to frag 0
  7130. for (i = fragIdx; i > 0; i--) {
  7131. updatePTS(fragments, i, i - 1);
  7132. }
  7133. // adjust fragment PTS/duration from seqnum to last frag
  7134. for (i = fragIdx; i < fragments.length - 1; i++) {
  7135. updatePTS(fragments, i, i + 1);
  7136. }
  7137. details.PTSKnown = true;
  7138. //logger.log(` frag start/end:${startPTS.toFixed(3)}/${endPTS.toFixed(3)}`);
  7139. return drift;
  7140. }
  7141. function mergeDetails(oldDetails, newDetails) {
  7142. var start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN,
  7143. end = Math.min(oldDetails.endSN, newDetails.endSN) - newDetails.startSN,
  7144. delta = newDetails.startSN - oldDetails.startSN,
  7145. oldfragments = oldDetails.fragments,
  7146. newfragments = newDetails.fragments,
  7147. ccOffset = 0,
  7148. PTSFrag;
  7149. // check if old/new playlists have fragments in common
  7150. if (end < start) {
  7151. newDetails.PTSKnown = false;
  7152. return;
  7153. }
  7154. // loop through overlapping SN and update startPTS , cc, and duration if any found
  7155. for (var i = start; i <= end; i++) {
  7156. var oldFrag = oldfragments[delta + i],
  7157. newFrag = newfragments[i];
  7158. if (newFrag && oldFrag) {
  7159. ccOffset = oldFrag.cc - newFrag.cc;
  7160. if (!isNaN(oldFrag.startPTS)) {
  7161. newFrag.start = newFrag.startPTS = oldFrag.startPTS;
  7162. newFrag.endPTS = oldFrag.endPTS;
  7163. newFrag.duration = oldFrag.duration;
  7164. newFrag.backtracked = oldFrag.backtracked;
  7165. newFrag.dropped = oldFrag.dropped;
  7166. PTSFrag = newFrag;
  7167. }
  7168. }
  7169. }
  7170. if (ccOffset) {
  7171. logger["b" /* logger */].log('discontinuity sliding from playlist, take drift into account');
  7172. for (i = 0; i < newfragments.length; i++) {
  7173. newfragments[i].cc += ccOffset;
  7174. }
  7175. }
  7176. // if at least one fragment contains PTS info, recompute PTS information for all fragments
  7177. if (PTSFrag) {
  7178. updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS);
  7179. } else {
  7180. // ensure that delta is within oldfragments range
  7181. // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61])
  7182. // in that case we also need to adjust start offset of all fragments
  7183. if (delta >= 0 && delta < oldfragments.length) {
  7184. // adjust start by sliding offset
  7185. var sliding = oldfragments[delta].start;
  7186. for (i = 0; i < newfragments.length; i++) {
  7187. newfragments[i].start += sliding;
  7188. }
  7189. }
  7190. }
  7191. // if we are here, it means we have fragments overlapping between
  7192. // old and new level. reliable PTS info is thus relying on old level
  7193. newDetails.PTSKnown = oldDetails.PTSKnown;
  7194. }
  7195. // CONCATENATED MODULE: ./src/utils/timeRanges.js
  7196. /**
  7197. * TimeRanges to string helper
  7198. */
  7199. var TimeRanges = {
  7200. toString: function toString(r) {
  7201. var log = '',
  7202. len = r.length;
  7203. for (var i = 0; i < len; i++) {
  7204. log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']';
  7205. }
  7206. return log;
  7207. }
  7208. };
  7209. /* harmony default export */
  7210. var timeRanges = (TimeRanges);
  7211. // CONCATENATED MODULE: ./src/utils/discontinuities.js
  7212. function findFirstFragWithCC(fragments, cc) {
  7213. var firstFrag = null;
  7214. for (var i = 0; i < fragments.length; i += 1) {
  7215. var currentFrag = fragments[i];
  7216. if (currentFrag && currentFrag.cc === cc) {
  7217. firstFrag = currentFrag;
  7218. break;
  7219. }
  7220. }
  7221. return firstFrag;
  7222. }
  7223. function findFragWithCC(fragments, CC) {
  7224. return binary_search.search(fragments, function (candidate) {
  7225. if (candidate.cc < CC) {
  7226. return 1;
  7227. } else if (candidate.cc > CC) {
  7228. return -1;
  7229. } else {
  7230. return 0;
  7231. }
  7232. });
  7233. }
  7234. function shouldAlignOnDiscontinuities(lastFrag, lastLevel, details) {
  7235. var shouldAlign = false;
  7236. if (lastLevel && lastLevel.details && details) {
  7237. if (details.endCC > details.startCC || lastFrag && lastFrag.cc < details.startCC) {
  7238. shouldAlign = true;
  7239. }
  7240. }
  7241. return shouldAlign;
  7242. }
  7243. // Find the first frag in the previous level which matches the CC of the first frag of the new level
  7244. function findDiscontinuousReferenceFrag(prevDetails, curDetails) {
  7245. var prevFrags = prevDetails.fragments;
  7246. var curFrags = curDetails.fragments;
  7247. if (!curFrags.length || !prevFrags.length) {
  7248. logger["b" /* logger */].log('No fragments to align');
  7249. return;
  7250. }
  7251. var prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc);
  7252. if (!prevStartFrag || prevStartFrag && !prevStartFrag.startPTS) {
  7253. logger["b" /* logger */].log('No frag in previous level to align on');
  7254. return;
  7255. }
  7256. return prevStartFrag;
  7257. }
  7258. function adjustPts(sliding, details) {
  7259. details.fragments.forEach(function (frag) {
  7260. if (frag) {
  7261. var start = frag.start + sliding;
  7262. frag.start = frag.startPTS = start;
  7263. frag.endPTS = start + frag.duration;
  7264. }
  7265. });
  7266. details.PTSKnown = true;
  7267. }
  7268. // If a change in CC is detected, the PTS can no longer be relied upon
  7269. // Attempt to align the level by using the last level - find the last frag matching the current CC and use it's PTS
  7270. // as a reference
  7271. function alignDiscontinuities(lastFrag, lastLevel, details) {
  7272. if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) {
  7273. var referenceFrag = findDiscontinuousReferenceFrag(lastLevel.details, details);
  7274. if (referenceFrag) {
  7275. logger["b" /* logger */].log('Adjusting PTS using last level due to CC increase within current level');
  7276. adjustPts(referenceFrag.start, details);
  7277. }
  7278. }
  7279. // try to align using programDateTime attribute (if available)
  7280. if (details.PTSKnown === false && lastLevel && lastLevel.details) {
  7281. // if last level sliding is 1000 and its first frag PROGRAM-DATE-TIME is 2017-08-20 1:10:00 AM
  7282. // and if new details first frag PROGRAM DATE-TIME is 2017-08-20 1:10:08 AM
  7283. // then we can deduce that playlist B sliding is 1000+8 = 1008s
  7284. var lastPDT = lastLevel.details.programDateTime;
  7285. var newPDT = details.programDateTime;
  7286. // date diff is in ms. frag.start is in seconds
  7287. var sliding = (newPDT - lastPDT) / 1000 + lastLevel.details.fragments[0].start;
  7288. if (!isNaN(sliding)) {
  7289. logger["b" /* logger */].log('adjusting PTS using programDateTime delta, sliding:' + sliding.toFixed(3));
  7290. adjustPts(sliding, details);
  7291. }
  7292. }
  7293. }
  7294. // CONCATENATED MODULE: ./src/task-loop.js
  7295. function task_loop__classCallCheck(instance, Constructor) {
  7296. if (!(instance instanceof Constructor)) {
  7297. throw new TypeError("Cannot call a class as a function");
  7298. }
  7299. }
  7300. function task_loop__possibleConstructorReturn(self, call) {
  7301. if (!self) {
  7302. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  7303. }
  7304. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  7305. }
  7306. function task_loop__inherits(subClass, superClass) {
  7307. if (typeof superClass !== "function" && superClass !== null) {
  7308. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  7309. }
  7310. subClass.prototype = Object.create(superClass && superClass.prototype, {
  7311. constructor: {
  7312. value: subClass,
  7313. enumerable: false,
  7314. writable: true,
  7315. configurable: true
  7316. }
  7317. });
  7318. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  7319. }
  7320. var TaskLoop = function (_EventHandler) {
  7321. task_loop__inherits(TaskLoop, _EventHandler);
  7322. function TaskLoop(hls) {
  7323. task_loop__classCallCheck(this, TaskLoop);
  7324. for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  7325. events[_key - 1] = arguments[_key];
  7326. }
  7327. var _this = task_loop__possibleConstructorReturn(this, _EventHandler.call.apply(_EventHandler, [this, hls].concat(events)));
  7328. _this._tickInterval = null;
  7329. _this._tickCallCount = 0;
  7330. return _this;
  7331. }
  7332. /**
  7333. * @override
  7334. */
  7335. TaskLoop.prototype.destroy = function destroy() {
  7336. this.clearInterval();
  7337. _EventHandler.prototype.destroy.call(this);
  7338. };
  7339. /**
  7340. * @returns {boolean}
  7341. */
  7342. TaskLoop.prototype.hasInterval = function hasInterval() {
  7343. return !isNaN(this._tickInterval);
  7344. };
  7345. /**
  7346. * @param {number} millis Interval time (ms)
  7347. * @returns {boolean} True when interval has been scheduled, false when already scheduled (no effect)
  7348. */
  7349. TaskLoop.prototype.setInterval = function (_setInterval) {
  7350. function setInterval(_x) {
  7351. return _setInterval.apply(this, arguments);
  7352. }
  7353. setInterval.toString = function () {
  7354. return _setInterval.toString();
  7355. };
  7356. return setInterval;
  7357. }(function (millis) {
  7358. if (!this._tickInterval) {
  7359. this._tickInterval = setInterval(this.tick.bind(this, false), millis);
  7360. return true;
  7361. }
  7362. return false;
  7363. });
  7364. /**
  7365. * @returns {boolean} True when interval was cleared, false when none was set (no effect)
  7366. */
  7367. TaskLoop.prototype.clearInterval = function (_clearInterval) {
  7368. function clearInterval() {
  7369. return _clearInterval.apply(this, arguments);
  7370. }
  7371. clearInterval.toString = function () {
  7372. return _clearInterval.toString();
  7373. };
  7374. return clearInterval;
  7375. }(function () {
  7376. if (this._tickInterval) {
  7377. clearInterval(this._tickInterval);
  7378. return true;
  7379. }
  7380. return false;
  7381. });
  7382. /**
  7383. *
  7384. * @param {Wether to force async} forceAsync
  7385. * @returns {boolean} True when async, false when sync
  7386. */
  7387. TaskLoop.prototype.tick = function tick() {
  7388. this._tickCallCount++;
  7389. if (this._tickCallCount === 1) {
  7390. this.doTick();
  7391. if (this._tickCallCount > 1) {
  7392. setTimeout(this.tick.bind(this), 0);
  7393. }
  7394. this._tickCallCount = 0;
  7395. }
  7396. };
  7397. /**
  7398. * For subclass to implement task logic
  7399. * @abstract
  7400. */
  7401. TaskLoop.prototype.doTick = function doTick() {
  7402. throw new Error('TaskLoop is abstract and `doLoop` must be implemented');
  7403. };
  7404. return TaskLoop;
  7405. }(event_handler);
  7406. /* harmony default export */
  7407. var task_loop = (TaskLoop);
  7408. // CONCATENATED MODULE: ./src/controller/stream-controller.js
  7409. var stream_controller__createClass = function () {
  7410. function defineProperties(target, props) {
  7411. for (var i = 0; i < props.length; i++) {
  7412. var descriptor = props[i];
  7413. descriptor.enumerable = descriptor.enumerable || false;
  7414. descriptor.configurable = true;
  7415. if ("value" in descriptor) descriptor.writable = true;
  7416. Object.defineProperty(target, descriptor.key, descriptor);
  7417. }
  7418. }
  7419. return function (Constructor, protoProps, staticProps) {
  7420. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  7421. if (staticProps) defineProperties(Constructor, staticProps);
  7422. return Constructor;
  7423. };
  7424. }();
  7425. function stream_controller__classCallCheck(instance, Constructor) {
  7426. if (!(instance instanceof Constructor)) {
  7427. throw new TypeError("Cannot call a class as a function");
  7428. }
  7429. }
  7430. function stream_controller__possibleConstructorReturn(self, call) {
  7431. if (!self) {
  7432. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  7433. }
  7434. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  7435. }
  7436. function stream_controller__inherits(subClass, superClass) {
  7437. if (typeof superClass !== "function" && superClass !== null) {
  7438. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  7439. }
  7440. subClass.prototype = Object.create(superClass && superClass.prototype, {
  7441. constructor: {
  7442. value: subClass,
  7443. enumerable: false,
  7444. writable: true,
  7445. configurable: true
  7446. }
  7447. });
  7448. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  7449. }
  7450. /*
  7451. * Stream Controller
  7452. */
  7453. var State = {
  7454. STOPPED: 'STOPPED',
  7455. IDLE: 'IDLE',
  7456. KEY_LOADING: 'KEY_LOADING',
  7457. FRAG_LOADING: 'FRAG_LOADING',
  7458. FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',
  7459. WAITING_LEVEL: 'WAITING_LEVEL',
  7460. PARSING: 'PARSING',
  7461. PARSED: 'PARSED',
  7462. BUFFER_FLUSHING: 'BUFFER_FLUSHING',
  7463. ENDED: 'ENDED',
  7464. ERROR: 'ERROR'
  7465. };
  7466. var stream_controller_StreamController = function (_TaskLoop) {
  7467. stream_controller__inherits(StreamController, _TaskLoop);
  7468. function StreamController(hls) {
  7469. stream_controller__classCallCheck(this, StreamController);
  7470. var _this = stream_controller__possibleConstructorReturn(this, _TaskLoop.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].MANIFEST_LOADING, events["a" /* default */].MANIFEST_PARSED, events["a" /* default */].LEVEL_LOADED, events["a" /* default */].KEY_LOADED, events["a" /* default */].FRAG_LOADED, events["a" /* default */].FRAG_LOAD_EMERGENCY_ABORTED, events["a" /* default */].FRAG_PARSING_INIT_SEGMENT, events["a" /* default */].FRAG_PARSING_DATA, events["a" /* default */].FRAG_PARSED, events["a" /* default */].ERROR, events["a" /* default */].AUDIO_TRACK_SWITCHING, events["a" /* default */].AUDIO_TRACK_SWITCHED, events["a" /* default */].BUFFER_CREATED, events["a" /* default */].BUFFER_APPENDED, events["a" /* default */].BUFFER_FLUSHED));
  7471. _this.config = hls.config;
  7472. _this.audioCodecSwap = false;
  7473. _this._state = State.STOPPED;
  7474. return _this;
  7475. }
  7476. StreamController.prototype.onHandlerDestroying = function onHandlerDestroying() {
  7477. this.stopLoad();
  7478. };
  7479. StreamController.prototype.onHandlerDestroyed = function onHandlerDestroyed() {
  7480. this.state = State.STOPPED;
  7481. };
  7482. StreamController.prototype.startLoad = function startLoad(startPosition) {
  7483. if (this.levels) {
  7484. var lastCurrentTime = this.lastCurrentTime,
  7485. hls = this.hls;
  7486. this.stopLoad();
  7487. this.setInterval(100);
  7488. this.level = -1;
  7489. this.fragLoadError = 0;
  7490. if (!this.startFragRequested) {
  7491. // determine load level
  7492. var startLevel = hls.startLevel;
  7493. if (startLevel === -1) {
  7494. // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level
  7495. startLevel = 0;
  7496. this.bitrateTest = true;
  7497. }
  7498. // set new level to playlist loader : this will trigger start level load
  7499. // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded
  7500. this.level = hls.nextLoadLevel = startLevel;
  7501. this.loadedmetadata = false;
  7502. }
  7503. // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime
  7504. if (lastCurrentTime > 0 && startPosition === -1) {
  7505. logger["b" /* logger */].log('override startPosition with lastCurrentTime @' + lastCurrentTime.toFixed(3));
  7506. startPosition = lastCurrentTime;
  7507. }
  7508. this.state = State.IDLE;
  7509. this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
  7510. this.tick();
  7511. } else {
  7512. this.forceStartLoad = true;
  7513. this.state = State.STOPPED;
  7514. }
  7515. };
  7516. StreamController.prototype.stopLoad = function stopLoad() {
  7517. var frag = this.fragCurrent;
  7518. if (frag) {
  7519. if (frag.loader) {
  7520. frag.loader.abort();
  7521. }
  7522. this.fragCurrent = null;
  7523. }
  7524. this.fragPrevious = null;
  7525. if (this.demuxer) {
  7526. this.demuxer.destroy();
  7527. this.demuxer = null;
  7528. }
  7529. this.state = State.STOPPED;
  7530. this.forceStartLoad = false;
  7531. };
  7532. StreamController.prototype.doTick = function doTick() {
  7533. switch (this.state) {
  7534. case State.ERROR:
  7535. //don't do anything in error state to avoid breaking further ...
  7536. break;
  7537. case State.BUFFER_FLUSHING:
  7538. // in buffer flushing state, reset fragLoadError counter
  7539. this.fragLoadError = 0;
  7540. break;
  7541. case State.IDLE:
  7542. this._doTickIdle();
  7543. break;
  7544. case State.WAITING_LEVEL:
  7545. var level = this.levels[this.level];
  7546. // check if playlist is already loaded
  7547. if (level && level.details) {
  7548. this.state = State.IDLE;
  7549. }
  7550. break;
  7551. case State.FRAG_LOADING_WAITING_RETRY:
  7552. var now = performance.now();
  7553. var retryDate = this.retryDate;
  7554. // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
  7555. if (!retryDate || now >= retryDate || this.media && this.media.seeking) {
  7556. logger["b" /* logger */].log('mediaController: retryDate reached, switch back to IDLE state');
  7557. this.state = State.IDLE;
  7558. }
  7559. break;
  7560. case State.ERROR:
  7561. case State.STOPPED:
  7562. case State.FRAG_LOADING:
  7563. case State.PARSING:
  7564. case State.PARSED:
  7565. case State.ENDED:
  7566. break;
  7567. default:
  7568. break;
  7569. }
  7570. // check buffer
  7571. this._checkBuffer();
  7572. // check/update current fragment
  7573. this._checkFragmentChanged();
  7574. };
  7575. // Ironically the "idle" state is the on we do the most logic in it seems ....
  7576. // NOTE: Maybe we could rather schedule a check for buffer length after half of the currently
  7577. // played segment, or on pause/play/seek instead of naively checking every 100ms?
  7578. StreamController.prototype._doTickIdle = function _doTickIdle() {
  7579. var hls = this.hls,
  7580. config = hls.config,
  7581. media = this.media;
  7582. // if start level not parsed yet OR
  7583. // if video not attached AND start fragment already requested OR start frag prefetch disable
  7584. // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment
  7585. if (this.levelLastLoaded === undefined || !media && (this.startFragRequested || !config.startFragPrefetch)) {
  7586. return;
  7587. }
  7588. // if we have not yet loaded any fragment, start loading from start position
  7589. var pos = void 0;
  7590. if (this.loadedmetadata) {
  7591. pos = media.currentTime;
  7592. } else {
  7593. pos = this.nextLoadPosition;
  7594. }
  7595. // determine next load level
  7596. var level = hls.nextLoadLevel,
  7597. levelInfo = this.levels[level];
  7598. if (!levelInfo) {
  7599. return;
  7600. }
  7601. var levelBitrate = levelInfo.bitrate,
  7602. maxBufLen = void 0;
  7603. // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s
  7604. if (levelBitrate) {
  7605. maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength);
  7606. } else {
  7607. maxBufLen = config.maxBufferLength;
  7608. }
  7609. maxBufLen = Math.min(maxBufLen, config.maxMaxBufferLength);
  7610. // determine next candidate fragment to be loaded, based on current position and end of buffer position
  7611. // ensure up to `config.maxMaxBufferLength` of buffer upfront
  7612. var bufferInfo = buffer_helper.bufferInfo(this.mediaBuffer ? this.mediaBuffer : media, pos, config.maxBufferHole),
  7613. bufferLen = bufferInfo.len;
  7614. // Stay idle if we are still with buffer margins
  7615. if (bufferLen >= maxBufLen) {
  7616. return;
  7617. }
  7618. // if buffer length is less than maxBufLen try to load a new fragment ...
  7619. logger["b" /* logger */].trace('buffer length of ' + bufferLen.toFixed(3) + ' is below max of ' + maxBufLen.toFixed(3) + '. checking for more payload ...');
  7620. // set next load level : this will trigger a playlist load if needed
  7621. this.level = hls.nextLoadLevel = level;
  7622. var levelDetails = levelInfo.details;
  7623. // if level info not retrieved yet, switch state and wait for level retrieval
  7624. // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
  7625. // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
  7626. if (levelDetails === undefined || levelDetails.live === true && this.levelLastLoaded !== level) {
  7627. this.state = State.WAITING_LEVEL;
  7628. return;
  7629. }
  7630. // we just got done loading the final fragment and there is no other buffered range after ...
  7631. // rationale is that in case there are any buffered ranges after, it means that there are unbuffered portion in between
  7632. // so we should not switch to ENDED in that case, to be able to buffer them
  7633. // dont switch to ENDED if we need to backtrack last fragment
  7634. var fragPrevious = this.fragPrevious;
  7635. if (!levelDetails.live && fragPrevious && !fragPrevious.backtracked && fragPrevious.sn === levelDetails.endSN && !bufferInfo.nextStart) {
  7636. // fragPrevious is last fragment. retrieve level duration using last frag start offset + duration
  7637. // real duration might be lower than initial duration if there are drifts between real frag duration and playlist signaling
  7638. var duration = Math.min(media.duration, fragPrevious.start + fragPrevious.duration);
  7639. // if everything (almost) til the end is buffered, let's signal eos
  7640. // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference (audio/video offsets...)
  7641. // tolerate up to one frag duration to cope with these cases.
  7642. // also cope with almost zero last frag duration (max last frag duration with 200ms) refer to https://github.com/video-dev/hls.js/pull/657
  7643. if (duration - Math.max(bufferInfo.end, fragPrevious.start) <= Math.max(0.2, fragPrevious.duration)) {
  7644. // Finalize the media stream
  7645. var data = {};
  7646. if (this.altAudio) {
  7647. data.type = 'video';
  7648. }
  7649. this.hls.trigger(events["a" /* default */].BUFFER_EOS, data);
  7650. this.state = State.ENDED;
  7651. return;
  7652. }
  7653. }
  7654. // if we have the levelDetails for the selected variant, lets continue enrichen our stream (load keys/fragments or trigger EOS, etc..)
  7655. this._fetchPayloadOrEos(pos, bufferInfo, levelDetails);
  7656. };
  7657. StreamController.prototype._fetchPayloadOrEos = function _fetchPayloadOrEos(pos, bufferInfo, levelDetails) {
  7658. var fragPrevious = this.fragPrevious,
  7659. level = this.level,
  7660. fragments = levelDetails.fragments,
  7661. fragLen = fragments.length;
  7662. // empty playlist
  7663. if (fragLen === 0) {
  7664. return;
  7665. }
  7666. // find fragment index, contiguous with end of buffer position
  7667. var start = fragments[0].start,
  7668. end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration,
  7669. bufferEnd = bufferInfo.end,
  7670. frag = void 0;
  7671. if (levelDetails.initSegment && !levelDetails.initSegment.data) {
  7672. frag = levelDetails.initSegment;
  7673. } else {
  7674. // in case of live playlist we need to ensure that requested position is not located before playlist start
  7675. if (levelDetails.live) {
  7676. var initialLiveManifestSize = this.config.initialLiveManifestSize;
  7677. if (fragLen < initialLiveManifestSize) {
  7678. logger["b" /* logger */].warn('Can not start playback of a level, reason: not enough fragments ' + fragLen + ' < ' + initialLiveManifestSize);
  7679. return;
  7680. }
  7681. frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen);
  7682. // if it explicitely returns null don't load any fragment and exit function now
  7683. if (frag === null) {
  7684. return;
  7685. }
  7686. } else {
  7687. // VoD playlist: if bufferEnd before start of playlist, load first fragment
  7688. if (bufferEnd < start) {
  7689. frag = fragments[0];
  7690. }
  7691. }
  7692. }
  7693. if (!frag) {
  7694. frag = this._findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails);
  7695. }
  7696. if (frag) {
  7697. this._loadFragmentOrKey(frag, level, levelDetails, pos, bufferEnd);
  7698. }
  7699. return;
  7700. };
  7701. StreamController.prototype._ensureFragmentAtLivePoint = function _ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen) {
  7702. var config = this.hls.config,
  7703. media = this.media;
  7704. var frag = void 0;
  7705. // check if requested position is within seekable boundaries :
  7706. //logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`);
  7707. var maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration;
  7708. if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) {
  7709. var liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails);
  7710. logger["b" /* logger */].log('buffer end: ' + bufferEnd.toFixed(3) + ' is located too far from the end of live sliding playlist, reset currentTime to : ' + liveSyncPosition.toFixed(3));
  7711. bufferEnd = liveSyncPosition;
  7712. if (media && media.readyState && media.duration > liveSyncPosition) {
  7713. media.currentTime = liveSyncPosition;
  7714. }
  7715. this.nextLoadPosition = liveSyncPosition;
  7716. }
  7717. // if end of buffer greater than live edge, don't load any fragment
  7718. // this could happen if live playlist intermittently slides in the past.
  7719. // level 1 loaded [182580161,182580167]
  7720. // level 1 loaded [182580162,182580169]
  7721. // Loading 182580168 of [182580162 ,182580169],level 1 ..
  7722. // Loading 182580169 of [182580162 ,182580169],level 1 ..
  7723. // level 1 loaded [182580162,182580168] <============= here we should have bufferEnd > end. in that case break to avoid reloading 182580168
  7724. // level 1 loaded [182580164,182580171]
  7725. //
  7726. // don't return null in case media not loaded yet (readystate === 0)
  7727. if (levelDetails.PTSKnown && bufferEnd > end && media && media.readyState) {
  7728. return null;
  7729. }
  7730. if (this.startFragRequested && !levelDetails.PTSKnown) {
  7731. /* we are switching level on live playlist, but we don't have any PTS info for that quality level ...
  7732. try to load frag matching with next SN.
  7733. even if SN are not synchronized between playlists, loading this frag will help us
  7734. compute playlist sliding and find the right one after in case it was not the right consecutive one */
  7735. if (fragPrevious) {
  7736. var targetSN = fragPrevious.sn + 1;
  7737. if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) {
  7738. var fragNext = fragments[targetSN - levelDetails.startSN];
  7739. if (fragPrevious.cc === fragNext.cc) {
  7740. frag = fragNext;
  7741. logger["b" /* logger */].log('live playlist, switching playlist, load frag with next SN: ' + frag.sn);
  7742. }
  7743. }
  7744. // next frag SN not available (or not with same continuity counter)
  7745. // look for a frag sharing the same CC
  7746. if (!frag) {
  7747. frag = binary_search.search(fragments, function (frag) {
  7748. return fragPrevious.cc - frag.cc;
  7749. });
  7750. if (frag) {
  7751. logger["b" /* logger */].log('live playlist, switching playlist, load frag with same CC: ' + frag.sn);
  7752. }
  7753. }
  7754. }
  7755. if (!frag) {
  7756. /* we have no idea about which fragment should be loaded.
  7757. so let's load mid fragment. it will help computing playlist sliding and find the right one
  7758. */
  7759. frag = fragments[Math.min(fragLen - 1, Math.round(fragLen / 2))];
  7760. logger["b" /* logger */].log('live playlist, switching playlist, unknown, load middle frag : ' + frag.sn);
  7761. }
  7762. }
  7763. return frag;
  7764. };
  7765. StreamController.prototype._findFragment = function _findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails) {
  7766. var config = this.hls.config;
  7767. var frag = void 0;
  7768. var foundFrag = void 0;
  7769. var maxFragLookUpTolerance = config.maxFragLookUpTolerance;
  7770. var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined;
  7771. var fragmentWithinToleranceTest = function fragmentWithinToleranceTest(candidate) {
  7772. // offset should be within fragment boundary - config.maxFragLookUpTolerance
  7773. // this is to cope with situations like
  7774. // bufferEnd = 9.991
  7775. // frag[Ø] : [0,10]
  7776. // frag[1] : [10,20]
  7777. // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
  7778. // frag start frag start+duration
  7779. // |-----------------------------|
  7780. // <---> <--->
  7781. // ...--------><-----------------------------><---------....
  7782. // previous frag matching fragment next frag
  7783. // return -1 return 0 return 1
  7784. //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
  7785. // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
  7786. var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0));
  7787. if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {
  7788. return 1;
  7789. } // if maxFragLookUpTolerance will have negative value then don't return -1 for first element
  7790. else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {
  7791. return -1;
  7792. }
  7793. return 0;
  7794. };
  7795. if (bufferEnd < end) {
  7796. if (bufferEnd > end - maxFragLookUpTolerance) {
  7797. maxFragLookUpTolerance = 0;
  7798. }
  7799. // Prefer the next fragment if it's within tolerance
  7800. if (fragNext && !fragmentWithinToleranceTest(fragNext)) {
  7801. foundFrag = fragNext;
  7802. } else {
  7803. foundFrag = binary_search.search(fragments, fragmentWithinToleranceTest);
  7804. }
  7805. } else {
  7806. // reach end of playlist
  7807. foundFrag = fragments[fragLen - 1];
  7808. }
  7809. if (foundFrag) {
  7810. frag = foundFrag;
  7811. var curSNIdx = frag.sn - levelDetails.startSN;
  7812. var sameLevel = fragPrevious && frag.level === fragPrevious.level;
  7813. var prevFrag = fragments[curSNIdx - 1];
  7814. var nextFrag = fragments[curSNIdx + 1];
  7815. //logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn);
  7816. if (fragPrevious && frag.sn === fragPrevious.sn) {
  7817. if (sameLevel && !frag.backtracked) {
  7818. if (frag.sn < levelDetails.endSN) {
  7819. var deltaPTS = fragPrevious.deltaPTS;
  7820. // if there is a significant delta between audio and video, larger than max allowed hole,
  7821. // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
  7822. // let's try to load previous fragment again to get last keyframe
  7823. // then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
  7824. if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped && curSNIdx) {
  7825. frag = prevFrag;
  7826. logger["b" /* logger */].warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this');
  7827. // decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
  7828. fragPrevious.loadCounter--;
  7829. } else {
  7830. frag = nextFrag;
  7831. logger["b" /* logger */].log('SN just loaded, load next one: ' + frag.sn);
  7832. }
  7833. } else {
  7834. frag = null;
  7835. }
  7836. } else if (frag.backtracked) {
  7837. // Only backtrack a max of 1 consecutive fragment to prevent sliding back too far when little or no frags start with keyframes
  7838. if (nextFrag && nextFrag.backtracked) {
  7839. logger["b" /* logger */].warn('Already backtracked from fragment ' + nextFrag.sn + ', will not backtrack to fragment ' + frag.sn + '. Loading fragment ' + nextFrag.sn);
  7840. frag = nextFrag;
  7841. } else {
  7842. // If a fragment has dropped frames and it's in a same level/sequence, load the previous fragment to try and find the keyframe
  7843. // Reset the dropped count now since it won't be reset until we parse the fragment again, which prevents infinite backtracking on the same segment
  7844. logger["b" /* logger */].warn('Loaded fragment with dropped frames, backtracking 1 segment to find a keyframe');
  7845. frag.dropped = 0;
  7846. if (prevFrag) {
  7847. if (prevFrag.loadCounter) {
  7848. prevFrag.loadCounter--;
  7849. }
  7850. frag = prevFrag;
  7851. frag.backtracked = true;
  7852. } else if (curSNIdx) {
  7853. // can't backtrack on very first fragment
  7854. frag = null;
  7855. }
  7856. }
  7857. }
  7858. }
  7859. }
  7860. return frag;
  7861. };
  7862. StreamController.prototype._loadFragmentOrKey = function _loadFragmentOrKey(frag, level, levelDetails, pos, bufferEnd) {
  7863. var hls = this.hls,
  7864. config = hls.config;
  7865. //logger.log('loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
  7866. if (frag.decryptdata && frag.decryptdata.uri != null && frag.decryptdata.key == null) {
  7867. logger["b" /* logger */].log('Loading key for ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level);
  7868. this.state = State.KEY_LOADING;
  7869. hls.trigger(events["a" /* default */].KEY_LOADING, {frag: frag});
  7870. } else {
  7871. logger["b" /* logger */].log('Loading ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level + ', currentTime:' + pos.toFixed(3) + ',bufferEnd:' + bufferEnd.toFixed(3));
  7872. // ensure that we are not reloading the same fragments in loop ...
  7873. if (this.fragLoadIdx !== undefined) {
  7874. this.fragLoadIdx++;
  7875. } else {
  7876. this.fragLoadIdx = 0;
  7877. }
  7878. if (frag.loadCounter) {
  7879. frag.loadCounter++;
  7880. var maxThreshold = config.fragLoadingLoopThreshold;
  7881. // if this frag has already been loaded 3 times, and if it has been reloaded recently
  7882. if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) {
  7883. hls.trigger(events["a" /* default */].ERROR, {
  7884. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  7885. details: errors["a" /* ErrorDetails */].FRAG_LOOP_LOADING_ERROR,
  7886. fatal: false,
  7887. frag: frag
  7888. });
  7889. return;
  7890. }
  7891. } else {
  7892. frag.loadCounter = 1;
  7893. }
  7894. frag.loadIdx = this.fragLoadIdx;
  7895. frag.autoLevel = hls.autoLevelEnabled;
  7896. frag.bitrateTest = this.bitrateTest;
  7897. this.fragCurrent = frag;
  7898. this.startFragRequested = true;
  7899. // Don't update nextLoadPosition for fragments which are not buffered
  7900. if (!isNaN(frag.sn) && !frag.bitrateTest) {
  7901. this.nextLoadPosition = frag.start + frag.duration;
  7902. }
  7903. hls.trigger(events["a" /* default */].FRAG_LOADING, {frag: frag});
  7904. // lazy demuxer init, as this could take some time ... do it during frag loading
  7905. if (!this.demuxer) {
  7906. this.demuxer = new demux_demuxer(hls, 'main');
  7907. }
  7908. this.state = State.FRAG_LOADING;
  7909. return;
  7910. }
  7911. };
  7912. StreamController.prototype.getBufferedFrag = function getBufferedFrag(position) {
  7913. return binary_search.search(this._bufferedFrags, function (frag) {
  7914. if (position < frag.startPTS) {
  7915. return -1;
  7916. } else if (position > frag.endPTS) {
  7917. return 1;
  7918. }
  7919. return 0;
  7920. });
  7921. };
  7922. StreamController.prototype.followingBufferedFrag = function followingBufferedFrag(frag) {
  7923. if (frag) {
  7924. // try to get range of next fragment (500ms after this range)
  7925. return this.getBufferedFrag(frag.endPTS + 0.5);
  7926. }
  7927. return null;
  7928. };
  7929. StreamController.prototype._checkFragmentChanged = function _checkFragmentChanged() {
  7930. var fragPlayingCurrent,
  7931. currentTime,
  7932. video = this.media;
  7933. if (video && video.readyState && video.seeking === false) {
  7934. currentTime = video.currentTime;
  7935. /* if video element is in seeked state, currentTime can only increase.
  7936. (assuming that playback rate is positive ...)
  7937. As sometimes currentTime jumps back to zero after a
  7938. media decode error, check this, to avoid seeking back to
  7939. wrong position after a media decode error
  7940. */
  7941. if (currentTime > video.playbackRate * this.lastCurrentTime) {
  7942. this.lastCurrentTime = currentTime;
  7943. }
  7944. if (buffer_helper.isBuffered(video, currentTime)) {
  7945. fragPlayingCurrent = this.getBufferedFrag(currentTime);
  7946. } else if (buffer_helper.isBuffered(video, currentTime + 0.1)) {
  7947. /* ensure that FRAG_CHANGED event is triggered at startup,
  7948. when first video frame is displayed and playback is paused.
  7949. add a tolerance of 100ms, in case current position is not buffered,
  7950. check if current pos+100ms is buffered and use that buffer range
  7951. for FRAG_CHANGED event reporting */
  7952. fragPlayingCurrent = this.getBufferedFrag(currentTime + 0.1);
  7953. }
  7954. if (fragPlayingCurrent) {
  7955. var fragPlaying = fragPlayingCurrent;
  7956. if (fragPlaying !== this.fragPlaying) {
  7957. this.hls.trigger(events["a" /* default */].FRAG_CHANGED, {frag: fragPlaying});
  7958. var fragPlayingLevel = fragPlaying.level;
  7959. if (!this.fragPlaying || this.fragPlaying.level !== fragPlayingLevel) {
  7960. this.hls.trigger(events["a" /* default */].LEVEL_SWITCHED, {level: fragPlayingLevel});
  7961. }
  7962. this.fragPlaying = fragPlaying;
  7963. }
  7964. }
  7965. }
  7966. };
  7967. /*
  7968. on immediate level switch :
  7969. - pause playback if playing
  7970. - cancel any pending load request
  7971. - and trigger a buffer flush
  7972. */
  7973. StreamController.prototype.immediateLevelSwitch = function immediateLevelSwitch() {
  7974. logger["b" /* logger */].log('immediateLevelSwitch');
  7975. if (!this.immediateSwitch) {
  7976. this.immediateSwitch = true;
  7977. var media = this.media,
  7978. previouslyPaused = void 0;
  7979. if (media) {
  7980. previouslyPaused = media.paused;
  7981. media.pause();
  7982. } else {
  7983. // don't restart playback after instant level switch in case media not attached
  7984. previouslyPaused = true;
  7985. }
  7986. this.previouslyPaused = previouslyPaused;
  7987. }
  7988. var fragCurrent = this.fragCurrent;
  7989. if (fragCurrent && fragCurrent.loader) {
  7990. fragCurrent.loader.abort();
  7991. }
  7992. this.fragCurrent = null;
  7993. // increase fragment load Index to avoid frag loop loading error after buffer flush
  7994. if (this.fragLoadIdx !== undefined) {
  7995. this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
  7996. }
  7997. // flush everything
  7998. this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
  7999. };
  8000. /*
  8001. on immediate level switch end, after new fragment has been buffered :
  8002. - nudge video decoder by slightly adjusting video currentTime (if currentTime buffered)
  8003. - resume the playback if needed
  8004. */
  8005. StreamController.prototype.immediateLevelSwitchEnd = function immediateLevelSwitchEnd() {
  8006. var media = this.media;
  8007. if (media && media.buffered.length) {
  8008. this.immediateSwitch = false;
  8009. if (buffer_helper.isBuffered(media, media.currentTime)) {
  8010. // only nudge if currentTime is buffered
  8011. media.currentTime -= 0.0001;
  8012. }
  8013. if (!this.previouslyPaused) {
  8014. media.play();
  8015. }
  8016. }
  8017. };
  8018. StreamController.prototype.nextLevelSwitch = function nextLevelSwitch() {
  8019. /* try to switch ASAP without breaking video playback :
  8020. in order to ensure smooth but quick level switching,
  8021. we need to find the next flushable buffer range
  8022. we should take into account new segment fetch time
  8023. */
  8024. var media = this.media;
  8025. // ensure that media is defined and that metadata are available (to retrieve currentTime)
  8026. if (media && media.readyState) {
  8027. var fetchdelay = void 0,
  8028. fragPlayingCurrent = void 0,
  8029. nextBufferedFrag = void 0;
  8030. if (this.fragLoadIdx !== undefined) {
  8031. // increase fragment load Index to avoid frag loop loading error after buffer flush
  8032. this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
  8033. }
  8034. fragPlayingCurrent = this.getBufferedFrag(media.currentTime);
  8035. if (fragPlayingCurrent && fragPlayingCurrent.startPTS > 1) {
  8036. // flush buffer preceding current fragment (flush until current fragment start offset)
  8037. // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
  8038. this.flushMainBuffer(0, fragPlayingCurrent.startPTS - 1);
  8039. }
  8040. if (!media.paused) {
  8041. // add a safety delay of 1s
  8042. var nextLevelId = this.hls.nextLoadLevel,
  8043. nextLevel = this.levels[nextLevelId],
  8044. fragLastKbps = this.fragLastKbps;
  8045. if (fragLastKbps && this.fragCurrent) {
  8046. fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1;
  8047. } else {
  8048. fetchdelay = 0;
  8049. }
  8050. } else {
  8051. fetchdelay = 0;
  8052. }
  8053. //logger.log('fetchdelay:'+fetchdelay);
  8054. // find buffer range that will be reached once new fragment will be fetched
  8055. nextBufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay);
  8056. if (nextBufferedFrag) {
  8057. // we can flush buffer range following this one without stalling playback
  8058. nextBufferedFrag = this.followingBufferedFrag(nextBufferedFrag);
  8059. if (nextBufferedFrag) {
  8060. // if we are here, we can also cancel any loading/demuxing in progress, as they are useless
  8061. var fragCurrent = this.fragCurrent;
  8062. if (fragCurrent && fragCurrent.loader) {
  8063. fragCurrent.loader.abort();
  8064. }
  8065. this.fragCurrent = null;
  8066. // start flush position is the start PTS of next buffered frag.
  8067. // we use frag.naxStartPTS which is max(audio startPTS, video startPTS).
  8068. // in case there is a small PTS Delta between audio and video, using maxStartPTS avoids flushing last samples from current fragment
  8069. this.flushMainBuffer(nextBufferedFrag.maxStartPTS, Number.POSITIVE_INFINITY);
  8070. }
  8071. }
  8072. }
  8073. };
  8074. StreamController.prototype.flushMainBuffer = function flushMainBuffer(startOffset, endOffset) {
  8075. this.state = State.BUFFER_FLUSHING;
  8076. var flushScope = {startOffset: startOffset, endOffset: endOffset};
  8077. // if alternate audio tracks are used, only flush video, otherwise flush everything
  8078. if (this.altAudio) {
  8079. flushScope.type = 'video';
  8080. }
  8081. this.hls.trigger(events["a" /* default */].BUFFER_FLUSHING, flushScope);
  8082. };
  8083. StreamController.prototype.onMediaAttached = function onMediaAttached(data) {
  8084. var media = this.media = this.mediaBuffer = data.media;
  8085. this.onvseeking = this.onMediaSeeking.bind(this);
  8086. this.onvseeked = this.onMediaSeeked.bind(this);
  8087. this.onvended = this.onMediaEnded.bind(this);
  8088. media.addEventListener('seeking', this.onvseeking);
  8089. media.addEventListener('seeked', this.onvseeked);
  8090. media.addEventListener('ended', this.onvended);
  8091. var config = this.config;
  8092. if (this.levels && config.autoStartLoad) {
  8093. this.hls.startLoad(config.startPosition);
  8094. }
  8095. };
  8096. StreamController.prototype.onMediaDetaching = function onMediaDetaching() {
  8097. var media = this.media;
  8098. if (media && media.ended) {
  8099. logger["b" /* logger */].log('MSE detaching and video ended, reset startPosition');
  8100. this.startPosition = this.lastCurrentTime = 0;
  8101. }
  8102. // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery
  8103. var levels = this.levels;
  8104. if (levels) {
  8105. // reset fragment load counter
  8106. levels.forEach(function (level) {
  8107. if (level.details) {
  8108. level.details.fragments.forEach(function (fragment) {
  8109. fragment.loadCounter = undefined;
  8110. fragment.backtracked = undefined;
  8111. });
  8112. }
  8113. });
  8114. }
  8115. // remove video listeners
  8116. if (media) {
  8117. media.removeEventListener('seeking', this.onvseeking);
  8118. media.removeEventListener('seeked', this.onvseeked);
  8119. media.removeEventListener('ended', this.onvended);
  8120. this.onvseeking = this.onvseeked = this.onvended = null;
  8121. }
  8122. this.media = this.mediaBuffer = null;
  8123. this.loadedmetadata = false;
  8124. this.stopLoad();
  8125. };
  8126. StreamController.prototype.onMediaSeeking = function onMediaSeeking() {
  8127. var media = this.media,
  8128. currentTime = media ? media.currentTime : undefined,
  8129. config = this.config;
  8130. if (!isNaN(currentTime)) {
  8131. logger["b" /* logger */].log('media seeking to ' + currentTime.toFixed(3));
  8132. }
  8133. var mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media;
  8134. var bufferInfo = buffer_helper.bufferInfo(mediaBuffer, currentTime, this.config.maxBufferHole);
  8135. if (this.state === State.FRAG_LOADING) {
  8136. var fragCurrent = this.fragCurrent;
  8137. // check if we are seeking to a unbuffered area AND if frag loading is in progress
  8138. if (bufferInfo.len === 0 && fragCurrent) {
  8139. var tolerance = config.maxFragLookUpTolerance,
  8140. fragStartOffset = fragCurrent.start - tolerance,
  8141. fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
  8142. // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything
  8143. if (currentTime < fragStartOffset || currentTime > fragEndOffset) {
  8144. if (fragCurrent.loader) {
  8145. logger["b" /* logger */].log('seeking outside of buffer while fragment load in progress, cancel fragment load');
  8146. fragCurrent.loader.abort();
  8147. }
  8148. this.fragCurrent = null;
  8149. this.fragPrevious = null;
  8150. // switch to IDLE state to load new fragment
  8151. this.state = State.IDLE;
  8152. } else {
  8153. logger["b" /* logger */].log('seeking outside of buffer but within currently loaded fragment range');
  8154. }
  8155. }
  8156. } else if (this.state === State.ENDED) {
  8157. // if seeking to unbuffered area, clean up fragPrevious
  8158. if (bufferInfo.len === 0) {
  8159. this.fragPrevious = 0;
  8160. }
  8161. // switch to IDLE state to check for potential new fragment
  8162. this.state = State.IDLE;
  8163. }
  8164. if (media) {
  8165. this.lastCurrentTime = currentTime;
  8166. }
  8167. // avoid reporting fragment loop loading error in case user is seeking several times on same position
  8168. if (this.state !== State.FRAG_LOADING && this.fragLoadIdx !== undefined) {
  8169. this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold;
  8170. }
  8171. // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
  8172. if (!this.loadedmetadata) {
  8173. this.nextLoadPosition = this.startPosition = currentTime;
  8174. }
  8175. // tick to speed up processing
  8176. this.tick();
  8177. };
  8178. StreamController.prototype.onMediaSeeked = function onMediaSeeked() {
  8179. var media = this.media,
  8180. currentTime = media ? media.currentTime : undefined;
  8181. if (!isNaN(currentTime)) {
  8182. logger["b" /* logger */].log('media seeked to ' + currentTime.toFixed(3));
  8183. }
  8184. // tick to speed up FRAGMENT_PLAYING triggering
  8185. this.tick();
  8186. };
  8187. StreamController.prototype.onMediaEnded = function onMediaEnded() {
  8188. logger["b" /* logger */].log('media ended');
  8189. // reset startPosition and lastCurrentTime to restart playback @ stream beginning
  8190. this.startPosition = this.lastCurrentTime = 0;
  8191. };
  8192. StreamController.prototype.onManifestLoading = function onManifestLoading() {
  8193. // reset buffer on manifest loading
  8194. logger["b" /* logger */].log('trigger BUFFER_RESET');
  8195. this.hls.trigger(events["a" /* default */].BUFFER_RESET);
  8196. this._bufferedFrags = [];
  8197. this.stalled = false;
  8198. this.startPosition = this.lastCurrentTime = 0;
  8199. };
  8200. StreamController.prototype.onManifestParsed = function onManifestParsed(data) {
  8201. var aac = false,
  8202. heaac = false,
  8203. codec;
  8204. data.levels.forEach(function (level) {
  8205. // detect if we have different kind of audio codecs used amongst playlists
  8206. codec = level.audioCodec;
  8207. if (codec) {
  8208. if (codec.indexOf('mp4a.40.2') !== -1) {
  8209. aac = true;
  8210. }
  8211. if (codec.indexOf('mp4a.40.5') !== -1) {
  8212. heaac = true;
  8213. }
  8214. }
  8215. });
  8216. this.audioCodecSwitch = aac && heaac;
  8217. if (this.audioCodecSwitch) {
  8218. logger["b" /* logger */].log('both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC');
  8219. }
  8220. this.levels = data.levels;
  8221. this.startFragRequested = false;
  8222. var config = this.config;
  8223. if (config.autoStartLoad || this.forceStartLoad) {
  8224. this.hls.startLoad(config.startPosition);
  8225. }
  8226. };
  8227. StreamController.prototype.onLevelLoaded = function onLevelLoaded(data) {
  8228. var newDetails = data.details;
  8229. var newLevelId = data.level;
  8230. var lastLevel = this.levels[this.levelLastLoaded];
  8231. var curLevel = this.levels[newLevelId];
  8232. var duration = newDetails.totalduration;
  8233. var sliding = 0;
  8234. logger["b" /* logger */].log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
  8235. if (newDetails.live) {
  8236. var curDetails = curLevel.details;
  8237. if (curDetails && newDetails.fragments.length > 0) {
  8238. // we already have details for that level, merge them
  8239. mergeDetails(curDetails, newDetails);
  8240. sliding = newDetails.fragments[0].start;
  8241. this.liveSyncPosition = this.computeLivePosition(sliding, curDetails);
  8242. if (newDetails.PTSKnown && !isNaN(sliding)) {
  8243. logger["b" /* logger */].log('live playlist sliding:' + sliding.toFixed(3));
  8244. } else {
  8245. logger["b" /* logger */].log('live playlist - outdated PTS, unknown sliding');
  8246. alignDiscontinuities(this.fragPrevious, lastLevel, newDetails);
  8247. }
  8248. } else {
  8249. logger["b" /* logger */].log('live playlist - first load, unknown sliding');
  8250. newDetails.PTSKnown = false;
  8251. alignDiscontinuities(this.fragPrevious, lastLevel, newDetails);
  8252. }
  8253. } else {
  8254. newDetails.PTSKnown = false;
  8255. }
  8256. // override level info
  8257. curLevel.details = newDetails;
  8258. this.levelLastLoaded = newLevelId;
  8259. this.hls.trigger(events["a" /* default */].LEVEL_UPDATED, {
  8260. details: newDetails,
  8261. level: newLevelId
  8262. });
  8263. if (this.startFragRequested === false) {
  8264. // compute start position if set to -1. use it straight away if value is defined
  8265. if (this.startPosition === -1 || this.lastCurrentTime === -1) {
  8266. // first, check if start time offset has been set in playlist, if yes, use this value
  8267. var startTimeOffset = newDetails.startTimeOffset;
  8268. if (!isNaN(startTimeOffset)) {
  8269. if (startTimeOffset < 0) {
  8270. logger["b" /* logger */].log('negative start time offset ' + startTimeOffset + ', count from end of last fragment');
  8271. startTimeOffset = sliding + duration + startTimeOffset;
  8272. }
  8273. logger["b" /* logger */].log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
  8274. this.startPosition = startTimeOffset;
  8275. } else {
  8276. // if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
  8277. if (newDetails.live) {
  8278. this.startPosition = this.computeLivePosition(sliding, newDetails);
  8279. logger["b" /* logger */].log('configure startPosition to ' + this.startPosition);
  8280. } else {
  8281. this.startPosition = 0;
  8282. }
  8283. }
  8284. this.lastCurrentTime = this.startPosition;
  8285. }
  8286. this.nextLoadPosition = this.startPosition;
  8287. }
  8288. // only switch batck to IDLE state if we were waiting for level to start downloading a new fragment
  8289. if (this.state === State.WAITING_LEVEL) {
  8290. this.state = State.IDLE;
  8291. }
  8292. //trigger handler right now
  8293. this.tick();
  8294. };
  8295. StreamController.prototype.onKeyLoaded = function onKeyLoaded() {
  8296. if (this.state === State.KEY_LOADING) {
  8297. this.state = State.IDLE;
  8298. this.tick();
  8299. }
  8300. };
  8301. StreamController.prototype.onFragLoaded = function onFragLoaded(data) {
  8302. var fragCurrent = this.fragCurrent,
  8303. fragLoaded = data.frag;
  8304. if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'main' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) {
  8305. var stats = data.stats,
  8306. currentLevel = this.levels[fragCurrent.level],
  8307. details = currentLevel.details;
  8308. logger["b" /* logger */].log('Loaded ' + fragCurrent.sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + fragCurrent.level);
  8309. // reset frag bitrate test in any case after frag loaded event
  8310. this.bitrateTest = false;
  8311. this.stats = stats;
  8312. // if this frag was loaded to perform a bitrate test AND if hls.nextLoadLevel is greater than 0
  8313. // then this means that we should be able to load a fragment at a higher quality level
  8314. if (fragLoaded.bitrateTest === true && this.hls.nextLoadLevel) {
  8315. // switch back to IDLE state ... we just loaded a fragment to determine adequate start bitrate and initialize autoswitch algo
  8316. this.state = State.IDLE;
  8317. this.startFragRequested = false;
  8318. stats.tparsed = stats.tbuffered = performance.now();
  8319. this.hls.trigger(events["a" /* default */].FRAG_BUFFERED, {
  8320. stats: stats,
  8321. frag: fragCurrent,
  8322. id: 'main'
  8323. });
  8324. this.tick();
  8325. } else if (fragLoaded.sn === 'initSegment') {
  8326. this.state = State.IDLE;
  8327. stats.tparsed = stats.tbuffered = performance.now();
  8328. details.initSegment.data = data.payload;
  8329. this.hls.trigger(events["a" /* default */].FRAG_BUFFERED, {
  8330. stats: stats,
  8331. frag: fragCurrent,
  8332. id: 'main'
  8333. });
  8334. this.tick();
  8335. } else {
  8336. this.state = State.PARSING;
  8337. // transmux the MPEG-TS data to ISO-BMFF segments
  8338. var duration = details.totalduration,
  8339. level = fragCurrent.level,
  8340. sn = fragCurrent.sn,
  8341. audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec;
  8342. if (this.audioCodecSwap) {
  8343. logger["b" /* logger */].log('swapping playlist audio codec');
  8344. if (audioCodec === undefined) {
  8345. audioCodec = this.lastAudioCodec;
  8346. }
  8347. if (audioCodec) {
  8348. if (audioCodec.indexOf('mp4a.40.5') !== -1) {
  8349. audioCodec = 'mp4a.40.2';
  8350. } else {
  8351. audioCodec = 'mp4a.40.5';
  8352. }
  8353. }
  8354. }
  8355. this.pendingBuffering = true;
  8356. this.appended = false;
  8357. logger["b" /* logger */].log('Parsing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level + ', cc ' + fragCurrent.cc);
  8358. var demuxer = this.demuxer;
  8359. if (!demuxer) {
  8360. demuxer = this.demuxer = new demux_demuxer(this.hls, 'main');
  8361. }
  8362. // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) and if media is not seeking (this is to overcome potential timestamp drifts between playlists and fragments)
  8363. var media = this.media;
  8364. var mediaSeeking = media && media.seeking;
  8365. var accurateTimeOffset = !mediaSeeking && (details.PTSKnown || !details.live);
  8366. var initSegmentData = details.initSegment ? details.initSegment.data : [];
  8367. demuxer.push(data.payload, initSegmentData, audioCodec, currentLevel.videoCodec, fragCurrent, duration, accurateTimeOffset, undefined);
  8368. }
  8369. }
  8370. this.fragLoadError = 0;
  8371. };
  8372. StreamController.prototype.onFragParsingInitSegment = function onFragParsingInitSegment(data) {
  8373. var fragCurrent = this.fragCurrent;
  8374. var fragNew = data.frag;
  8375. if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
  8376. var tracks = data.tracks,
  8377. trackName,
  8378. track;
  8379. // if audio track is expected to come from audio stream controller, discard any coming from main
  8380. if (tracks.audio && this.altAudio) {
  8381. delete tracks.audio;
  8382. }
  8383. // include levelCodec in audio and video tracks
  8384. track = tracks.audio;
  8385. if (track) {
  8386. var audioCodec = this.levels[this.level].audioCodec,
  8387. ua = navigator.userAgent.toLowerCase();
  8388. if (audioCodec && this.audioCodecSwap) {
  8389. logger["b" /* logger */].log('swapping playlist audio codec');
  8390. if (audioCodec.indexOf('mp4a.40.5') !== -1) {
  8391. audioCodec = 'mp4a.40.2';
  8392. } else {
  8393. audioCodec = 'mp4a.40.5';
  8394. }
  8395. }
  8396. // in case AAC and HE-AAC audio codecs are signalled in manifest
  8397. // force HE-AAC , as it seems that most browsers prefers that way,
  8398. // except for mono streams OR on FF
  8399. // these conditions might need to be reviewed ...
  8400. if (this.audioCodecSwitch) {
  8401. // don't force HE-AAC if mono stream
  8402. if (track.metadata.channelCount !== 1 &&
  8403. // don't force HE-AAC if firefox
  8404. ua.indexOf('firefox') === -1) {
  8405. audioCodec = 'mp4a.40.5';
  8406. }
  8407. }
  8408. // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise
  8409. if (ua.indexOf('android') !== -1 && track.container !== 'audio/mpeg') {
  8410. // Exclude mpeg audio
  8411. audioCodec = 'mp4a.40.2';
  8412. logger["b" /* logger */].log('Android: force audio codec to ' + audioCodec);
  8413. }
  8414. track.levelCodec = audioCodec;
  8415. track.id = data.id;
  8416. }
  8417. track = tracks.video;
  8418. if (track) {
  8419. track.levelCodec = this.levels[this.level].videoCodec;
  8420. track.id = data.id;
  8421. }
  8422. this.hls.trigger(events["a" /* default */].BUFFER_CODECS, tracks);
  8423. // loop through tracks that are going to be provided to bufferController
  8424. for (trackName in tracks) {
  8425. track = tracks[trackName];
  8426. logger["b" /* logger */].log('main track:' + trackName + ',container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']');
  8427. var initSegment = track.initSegment;
  8428. if (initSegment) {
  8429. this.appended = true;
  8430. // arm pending Buffering flag before appending a segment
  8431. this.pendingBuffering = true;
  8432. this.hls.trigger(events["a" /* default */].BUFFER_APPENDING, {
  8433. type: trackName,
  8434. data: initSegment,
  8435. parent: 'main',
  8436. content: 'initSegment'
  8437. });
  8438. }
  8439. }
  8440. //trigger handler right now
  8441. this.tick();
  8442. }
  8443. };
  8444. StreamController.prototype.onFragParsingData = function onFragParsingData(data) {
  8445. var _this2 = this;
  8446. var fragCurrent = this.fragCurrent;
  8447. var fragNew = data.frag;
  8448. if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && !(data.type === 'audio' && this.altAudio) && // filter out main audio if audio track is loaded through audio stream controller
  8449. this.state === State.PARSING) {
  8450. var level = this.levels[this.level],
  8451. frag = fragCurrent;
  8452. if (isNaN(data.endPTS)) {
  8453. data.endPTS = data.startPTS + fragCurrent.duration;
  8454. data.endDTS = data.startDTS + fragCurrent.duration;
  8455. }
  8456. logger["b" /* logger */].log('Parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb + ',dropped:' + (data.dropped || 0));
  8457. // Detect gaps in a fragment and try to fix it by finding a keyframe in the previous fragment (see _findFragments)
  8458. if (data.type === 'video') {
  8459. frag.dropped = data.dropped;
  8460. if (frag.dropped) {
  8461. if (!frag.backtracked) {
  8462. var levelDetails = level.details;
  8463. if (levelDetails && frag.sn === levelDetails.startSN) {
  8464. logger["b" /* logger */].warn('missing video frame(s) on first frag, appending with gap');
  8465. } else {
  8466. logger["b" /* logger */].warn('missing video frame(s), backtracking fragment');
  8467. // Return back to the IDLE state without appending to buffer
  8468. // Causes findFragments to backtrack a segment and find the keyframe
  8469. // Audio fragments arriving before video sets the nextLoadPosition, causing _findFragments to skip the backtracked fragment
  8470. frag.backtracked = true;
  8471. this.nextLoadPosition = data.startPTS;
  8472. this.state = State.IDLE;
  8473. this.fragPrevious = frag;
  8474. this.tick();
  8475. return;
  8476. }
  8477. } else {
  8478. logger["b" /* logger */].warn('Already backtracked on this fragment, appending with the gap');
  8479. }
  8480. } else {
  8481. // Only reset the backtracked flag if we've loaded the frag without any dropped frames
  8482. frag.backtracked = false;
  8483. }
  8484. }
  8485. var drift = updateFragPTSDTS(level.details, frag, data.startPTS, data.endPTS, data.startDTS, data.endDTS),
  8486. hls = this.hls;
  8487. hls.trigger(events["a" /* default */].LEVEL_PTS_UPDATED, {
  8488. details: level.details,
  8489. level: this.level,
  8490. drift: drift,
  8491. type: data.type,
  8492. start: data.startPTS,
  8493. end: data.endPTS
  8494. });
  8495. // has remuxer dropped video frames located before first keyframe ?
  8496. [data.data1, data.data2].forEach(function (buffer) {
  8497. // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending)
  8498. // in that case it is useless to append following segments
  8499. if (buffer && buffer.length && _this2.state === State.PARSING) {
  8500. _this2.appended = true;
  8501. // arm pending Buffering flag before appending a segment
  8502. _this2.pendingBuffering = true;
  8503. hls.trigger(events["a" /* default */].BUFFER_APPENDING, {
  8504. type: data.type,
  8505. data: buffer,
  8506. parent: 'main',
  8507. content: 'data'
  8508. });
  8509. }
  8510. });
  8511. //trigger handler right now
  8512. this.tick();
  8513. }
  8514. };
  8515. StreamController.prototype.onFragParsed = function onFragParsed(data) {
  8516. var fragCurrent = this.fragCurrent;
  8517. var fragNew = data.frag;
  8518. if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
  8519. this.stats.tparsed = performance.now();
  8520. this.state = State.PARSED;
  8521. this._checkAppendedParsed();
  8522. }
  8523. };
  8524. StreamController.prototype.onAudioTrackSwitching = function onAudioTrackSwitching(data) {
  8525. // if any URL found on new audio track, it is an alternate audio track
  8526. var altAudio = !!data.url,
  8527. trackId = data.id;
  8528. // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
  8529. // don't do anything if we switch to alt audio: audio stream controller is handling it.
  8530. // we will just have to change buffer scheduling on audioTrackSwitched
  8531. if (!altAudio) {
  8532. if (this.mediaBuffer !== this.media) {
  8533. logger["b" /* logger */].log('switching on main audio, use media.buffered to schedule main fragment loading');
  8534. this.mediaBuffer = this.media;
  8535. var fragCurrent = this.fragCurrent;
  8536. // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch
  8537. if (fragCurrent.loader) {
  8538. logger["b" /* logger */].log('switching to main audio track, cancel main fragment load');
  8539. fragCurrent.loader.abort();
  8540. }
  8541. this.fragCurrent = null;
  8542. this.fragPrevious = null;
  8543. // destroy demuxer to force init segment generation (following audio switch)
  8544. if (this.demuxer) {
  8545. this.demuxer.destroy();
  8546. this.demuxer = null;
  8547. }
  8548. // switch to IDLE state to load new fragment
  8549. this.state = State.IDLE;
  8550. }
  8551. var hls = this.hls;
  8552. // switching to main audio, flush all audio and trigger track switched
  8553. hls.trigger(events["a" /* default */].BUFFER_FLUSHING, {
  8554. startOffset: 0,
  8555. endOffset: Number.POSITIVE_INFINITY,
  8556. type: 'audio'
  8557. });
  8558. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHED, {id: trackId});
  8559. this.altAudio = false;
  8560. }
  8561. };
  8562. StreamController.prototype.onAudioTrackSwitched = function onAudioTrackSwitched(data) {
  8563. var trackId = data.id,
  8564. altAudio = !!this.hls.audioTracks[trackId].url;
  8565. if (altAudio) {
  8566. var videoBuffer = this.videoBuffer;
  8567. // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered
  8568. if (videoBuffer && this.mediaBuffer !== videoBuffer) {
  8569. logger["b" /* logger */].log('switching on alternate audio, use video.buffered to schedule main fragment loading');
  8570. this.mediaBuffer = videoBuffer;
  8571. }
  8572. }
  8573. this.altAudio = altAudio;
  8574. this.tick();
  8575. };
  8576. StreamController.prototype.onBufferCreated = function onBufferCreated(data) {
  8577. var tracks = data.tracks,
  8578. mediaTrack = void 0,
  8579. name = void 0,
  8580. alternate = false;
  8581. for (var type in tracks) {
  8582. var track = tracks[type];
  8583. if (track.id === 'main') {
  8584. name = type;
  8585. mediaTrack = track;
  8586. // keep video source buffer reference
  8587. if (type === 'video') {
  8588. this.videoBuffer = tracks[type].buffer;
  8589. }
  8590. } else {
  8591. alternate = true;
  8592. }
  8593. }
  8594. if (alternate && mediaTrack) {
  8595. logger["b" /* logger */].log('alternate track found, use ' + name + '.buffered to schedule main fragment loading');
  8596. this.mediaBuffer = mediaTrack.buffer;
  8597. } else {
  8598. this.mediaBuffer = this.media;
  8599. }
  8600. };
  8601. StreamController.prototype.onBufferAppended = function onBufferAppended(data) {
  8602. if (data.parent === 'main') {
  8603. var state = this.state;
  8604. if (state === State.PARSING || state === State.PARSED) {
  8605. // check if all buffers have been appended
  8606. this.pendingBuffering = data.pending > 0;
  8607. this._checkAppendedParsed();
  8608. }
  8609. }
  8610. };
  8611. StreamController.prototype._checkAppendedParsed = function _checkAppendedParsed() {
  8612. //trigger handler right now
  8613. if (this.state === State.PARSED && (!this.appended || !this.pendingBuffering)) {
  8614. var frag = this.fragCurrent;
  8615. if (frag) {
  8616. var media = this.mediaBuffer ? this.mediaBuffer : this.media;
  8617. logger["b" /* logger */].log('main buffered : ' + timeRanges.toString(media.buffered));
  8618. // filter fragments potentially evicted from buffer. this is to avoid memleak on live streams
  8619. var bufferedFrags = this._bufferedFrags.filter(function (frag) {
  8620. return buffer_helper.isBuffered(media, (frag.startPTS + frag.endPTS) / 2);
  8621. });
  8622. // push new range
  8623. bufferedFrags.push(frag);
  8624. // sort frags, as we use BinarySearch for lookup in getBufferedFrag ...
  8625. this._bufferedFrags = bufferedFrags.sort(function (a, b) {
  8626. return a.startPTS - b.startPTS;
  8627. });
  8628. this.fragPrevious = frag;
  8629. var stats = this.stats;
  8630. stats.tbuffered = performance.now();
  8631. // we should get rid of this.fragLastKbps
  8632. this.fragLastKbps = Math.round(8 * stats.total / (stats.tbuffered - stats.tfirst));
  8633. this.hls.trigger(events["a" /* default */].FRAG_BUFFERED, {
  8634. stats: stats,
  8635. frag: frag,
  8636. id: 'main'
  8637. });
  8638. this.state = State.IDLE;
  8639. }
  8640. this.tick();
  8641. }
  8642. };
  8643. StreamController.prototype.onError = function onError(data) {
  8644. var frag = data.frag || this.fragCurrent;
  8645. // don't handle frag error not related to main fragment
  8646. if (frag && frag.type !== 'main') {
  8647. return;
  8648. }
  8649. // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end
  8650. var mediaBuffered = !!this.media && buffer_helper.isBuffered(this.media, this.media.currentTime) && buffer_helper.isBuffered(this.media, this.media.currentTime + 0.5);
  8651. switch (data.details) {
  8652. case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
  8653. case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
  8654. case errors["a" /* ErrorDetails */].KEY_LOAD_ERROR:
  8655. case errors["a" /* ErrorDetails */].KEY_LOAD_TIMEOUT:
  8656. if (!data.fatal) {
  8657. // keep retrying until the limit will be reached
  8658. if (this.fragLoadError + 1 <= this.config.fragLoadingMaxRetry) {
  8659. // exponential backoff capped to config.fragLoadingMaxRetryTimeout
  8660. var delay = Math.min(Math.pow(2, this.fragLoadError) * this.config.fragLoadingRetryDelay, this.config.fragLoadingMaxRetryTimeout);
  8661. // reset load counter to avoid frag loop loading error
  8662. frag.loadCounter = 0;
  8663. logger["b" /* logger */].warn('mediaController: frag loading failed, retry in ' + delay + ' ms');
  8664. this.retryDate = performance.now() + delay;
  8665. // retry loading state
  8666. // if loadedmetadata is not set, it means that we are emergency switch down on first frag
  8667. // in that case, reset startFragRequested flag
  8668. if (!this.loadedmetadata) {
  8669. this.startFragRequested = false;
  8670. this.nextLoadPosition = this.startPosition;
  8671. }
  8672. this.fragLoadError++;
  8673. this.state = State.FRAG_LOADING_WAITING_RETRY;
  8674. } else {
  8675. logger["b" /* logger */].error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
  8676. // switch error to fatal
  8677. data.fatal = true;
  8678. this.state = State.ERROR;
  8679. }
  8680. }
  8681. break;
  8682. case errors["a" /* ErrorDetails */].FRAG_LOOP_LOADING_ERROR:
  8683. if (!data.fatal) {
  8684. // if buffer is not empty
  8685. if (mediaBuffered) {
  8686. // try to reduce max buffer length : rationale is that we could get
  8687. // frag loop loading error because of buffer eviction
  8688. this._reduceMaxBufferLength(frag.duration);
  8689. this.state = State.IDLE;
  8690. } else {
  8691. // buffer empty. report as fatal if in manual mode or if lowest level.
  8692. // level controller takes care of emergency switch down logic
  8693. if (!frag.autoLevel || frag.level === 0) {
  8694. // switch error to fatal
  8695. data.fatal = true;
  8696. this.state = State.ERROR;
  8697. }
  8698. }
  8699. }
  8700. break;
  8701. case errors["a" /* ErrorDetails */].LEVEL_LOAD_ERROR:
  8702. case errors["a" /* ErrorDetails */].LEVEL_LOAD_TIMEOUT:
  8703. if (this.state !== State.ERROR) {
  8704. if (data.fatal) {
  8705. // if fatal error, stop processing
  8706. this.state = State.ERROR;
  8707. logger["b" /* logger */].warn('streamController: ' + data.details + ',switch to ' + this.state + ' state ...');
  8708. } else {
  8709. // in case of non fatal error while loading level, if level controller is not retrying to load level , switch back to IDLE
  8710. if (!data.levelRetry && this.state === State.WAITING_LEVEL) {
  8711. this.state = State.IDLE;
  8712. }
  8713. }
  8714. }
  8715. break;
  8716. case errors["a" /* ErrorDetails */].BUFFER_FULL_ERROR:
  8717. // if in appending state
  8718. if (data.parent === 'main' && (this.state === State.PARSING || this.state === State.PARSED)) {
  8719. // reduce max buf len if current position is buffered
  8720. if (mediaBuffered) {
  8721. this._reduceMaxBufferLength(this.config.maxBufferLength);
  8722. this.state = State.IDLE;
  8723. } else {
  8724. // current position is not buffered, but browser is still complaining about buffer full error
  8725. // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708
  8726. // in that case flush the whole buffer to recover
  8727. logger["b" /* logger */].warn('buffer full error also media.currentTime is not buffered, flush everything');
  8728. this.fragCurrent = null;
  8729. // flush everything
  8730. this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
  8731. }
  8732. }
  8733. break;
  8734. default:
  8735. break;
  8736. }
  8737. };
  8738. StreamController.prototype._reduceMaxBufferLength = function _reduceMaxBufferLength(minLength) {
  8739. var config = this.config;
  8740. if (config.maxMaxBufferLength >= minLength) {
  8741. // reduce max buffer length as it might be too high. we do this to avoid loop flushing ...
  8742. config.maxMaxBufferLength /= 2;
  8743. logger["b" /* logger */].warn('main:reduce max buffer length to ' + config.maxMaxBufferLength + 's');
  8744. if (this.fragLoadIdx !== undefined) {
  8745. // increase fragment load Index to avoid frag loop loading error after buffer flush
  8746. this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold;
  8747. }
  8748. }
  8749. };
  8750. StreamController.prototype._checkBuffer = function _checkBuffer() {
  8751. var media = this.media,
  8752. config = this.config;
  8753. // if ready state different from HAVE_NOTHING (numeric value 0), we are allowed to seek
  8754. if (media && media.readyState) {
  8755. var currentTime = media.currentTime,
  8756. mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media,
  8757. buffered = mediaBuffer.buffered;
  8758. // adjust currentTime to start position on loaded metadata
  8759. if (!this.loadedmetadata && buffered.length) {
  8760. this.loadedmetadata = true;
  8761. // only adjust currentTime if different from startPosition or if startPosition not buffered
  8762. // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
  8763. var startPosition = media.seeking ? currentTime : this.startPosition,
  8764. startPositionBuffered = buffer_helper.isBuffered(mediaBuffer, startPosition),
  8765. firstbufferedPosition = buffered.start(0),
  8766. startNotBufferedButClose = !startPositionBuffered && Math.abs(startPosition - firstbufferedPosition) < config.maxSeekHole;
  8767. // if currentTime not matching with expected startPosition or startPosition not buffered but close to first buffered
  8768. if (currentTime !== startPosition || startNotBufferedButClose) {
  8769. logger["b" /* logger */].log('target start position:' + startPosition);
  8770. // if startPosition not buffered, let's seek to buffered.start(0)
  8771. if (startNotBufferedButClose) {
  8772. startPosition = firstbufferedPosition;
  8773. logger["b" /* logger */].log('target start position not buffered, seek to buffered.start(0) ' + startPosition);
  8774. }
  8775. logger["b" /* logger */].log('adjust currentTime from ' + currentTime + ' to ' + startPosition);
  8776. media.currentTime = startPosition;
  8777. }
  8778. } else if (this.immediateSwitch) {
  8779. this.immediateLevelSwitchEnd();
  8780. } else {
  8781. var bufferInfo = buffer_helper.bufferInfo(media, currentTime, 0),
  8782. expectedPlaying = !(media.paused || // not playing when media is paused
  8783. media.ended || // not playing when media is ended
  8784. media.buffered.length === 0),
  8785. // not playing if nothing buffered
  8786. jumpThreshold = 0.5,
  8787. // tolerance needed as some browsers stalls playback before reaching buffered range end
  8788. playheadMoving = currentTime !== this.lastCurrentTime;
  8789. if (playheadMoving) {
  8790. // played moving, but was previously stalled => now not stuck anymore
  8791. if (this.stallReported) {
  8792. logger["b" /* logger */].warn('playback not stuck anymore @' + currentTime + ', after ' + Math.round(performance.now() - this.stalled) + 'ms');
  8793. this.stallReported = false;
  8794. }
  8795. this.stalled = undefined;
  8796. this.nudgeRetry = 0;
  8797. } else {
  8798. // playhead not moving
  8799. if (expectedPlaying) {
  8800. // playhead not moving BUT media expected to play
  8801. var tnow = performance.now();
  8802. var hls = this.hls;
  8803. if (!this.stalled) {
  8804. // stall just detected, store current time
  8805. this.stalled = tnow;
  8806. this.stallReported = false;
  8807. } else {
  8808. // playback already stalled, check stalling duration
  8809. // if stalling for more than a given threshold, let's try to recover
  8810. var stalledDuration = tnow - this.stalled;
  8811. var bufferLen = bufferInfo.len;
  8812. var nudgeRetry = this.nudgeRetry || 0;
  8813. // have we reached stall deadline ?
  8814. if (bufferLen <= jumpThreshold && stalledDuration > config.lowBufferWatchdogPeriod * 1000) {
  8815. // report stalled error once
  8816. if (!this.stallReported) {
  8817. this.stallReported = true;
  8818. logger["b" /* logger */].warn('playback stalling in low buffer @' + currentTime);
  8819. hls.trigger(events["a" /* default */].ERROR, {
  8820. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  8821. details: errors["a" /* ErrorDetails */].BUFFER_STALLED_ERROR,
  8822. fatal: false,
  8823. buffer: bufferLen
  8824. });
  8825. }
  8826. // if buffer len is below threshold, try to jump to start of next buffer range if close
  8827. // no buffer available @ currentTime, check if next buffer is close (within a config.maxSeekHole second range)
  8828. var nextBufferStart = bufferInfo.nextStart,
  8829. delta = nextBufferStart - currentTime;
  8830. if (nextBufferStart && delta < config.maxSeekHole && delta > 0) {
  8831. this.nudgeRetry = ++nudgeRetry;
  8832. var nudgeOffset = nudgeRetry * config.nudgeOffset;
  8833. // next buffer is close ! adjust currentTime to nextBufferStart
  8834. // this will ensure effective video decoding
  8835. logger["b" /* logger */].log('adjust currentTime from ' + media.currentTime + ' to next buffered @ ' + nextBufferStart + ' + nudge ' + nudgeOffset);
  8836. media.currentTime = nextBufferStart + nudgeOffset;
  8837. // reset stalled so to rearm watchdog timer
  8838. this.stalled = undefined;
  8839. hls.trigger(events["a" /* default */].ERROR, {
  8840. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  8841. details: errors["a" /* ErrorDetails */].BUFFER_SEEK_OVER_HOLE,
  8842. fatal: false,
  8843. hole: nextBufferStart + nudgeOffset - currentTime
  8844. });
  8845. }
  8846. } else if (bufferLen > jumpThreshold && stalledDuration > config.highBufferWatchdogPeriod * 1000) {
  8847. // report stalled error once
  8848. if (!this.stallReported) {
  8849. this.stallReported = true;
  8850. logger["b" /* logger */].warn('playback stalling in high buffer @' + currentTime);
  8851. hls.trigger(events["a" /* default */].ERROR, {
  8852. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  8853. details: errors["a" /* ErrorDetails */].BUFFER_STALLED_ERROR,
  8854. fatal: false,
  8855. buffer: bufferLen
  8856. });
  8857. }
  8858. // reset stalled so to rearm watchdog timer
  8859. this.stalled = undefined;
  8860. this.nudgeRetry = ++nudgeRetry;
  8861. if (nudgeRetry < config.nudgeMaxRetry) {
  8862. var _currentTime = media.currentTime;
  8863. var targetTime = _currentTime + nudgeRetry * config.nudgeOffset;
  8864. logger["b" /* logger */].log('adjust currentTime from ' + _currentTime + ' to ' + targetTime);
  8865. // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
  8866. media.currentTime = targetTime;
  8867. hls.trigger(events["a" /* default */].ERROR, {
  8868. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  8869. details: errors["a" /* ErrorDetails */].BUFFER_NUDGE_ON_STALL,
  8870. fatal: false
  8871. });
  8872. } else {
  8873. logger["b" /* logger */].error('still stuck in high buffer @' + currentTime + ' after ' + config.nudgeMaxRetry + ', raise fatal error');
  8874. hls.trigger(events["a" /* default */].ERROR, {
  8875. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  8876. details: errors["a" /* ErrorDetails */].BUFFER_STALLED_ERROR,
  8877. fatal: true
  8878. });
  8879. }
  8880. }
  8881. }
  8882. }
  8883. }
  8884. }
  8885. }
  8886. };
  8887. StreamController.prototype.onFragLoadEmergencyAborted = function onFragLoadEmergencyAborted() {
  8888. this.state = State.IDLE;
  8889. // if loadedmetadata is not set, it means that we are emergency switch down on first frag
  8890. // in that case, reset startFragRequested flag
  8891. if (!this.loadedmetadata) {
  8892. this.startFragRequested = false;
  8893. this.nextLoadPosition = this.startPosition;
  8894. }
  8895. this.tick();
  8896. };
  8897. StreamController.prototype.onBufferFlushed = function onBufferFlushed() {
  8898. /* after successful buffer flushing, filter flushed fragments from bufferedFrags
  8899. use mediaBuffered instead of media (so that we will check against video.buffered ranges in case of alt audio track)
  8900. */
  8901. var media = this.mediaBuffer ? this.mediaBuffer : this.media;
  8902. this._bufferedFrags = this._bufferedFrags.filter(function (frag) {
  8903. return buffer_helper.isBuffered(media, (frag.startPTS + frag.endPTS) / 2);
  8904. });
  8905. if (this.fragLoadIdx !== undefined) {
  8906. // increase fragment load Index to avoid frag loop loading error after buffer flush
  8907. this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
  8908. }
  8909. // move to IDLE once flush complete. this should trigger new fragment loading
  8910. this.state = State.IDLE;
  8911. // reset reference to frag
  8912. this.fragPrevious = null;
  8913. };
  8914. StreamController.prototype.swapAudioCodec = function swapAudioCodec() {
  8915. this.audioCodecSwap = !this.audioCodecSwap;
  8916. };
  8917. StreamController.prototype.computeLivePosition = function computeLivePosition(sliding, levelDetails) {
  8918. var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration;
  8919. return sliding + Math.max(0, levelDetails.totalduration - targetLatency);
  8920. };
  8921. stream_controller__createClass(StreamController, [{
  8922. key: 'state',
  8923. set: function set(nextState) {
  8924. if (this.state !== nextState) {
  8925. var previousState = this.state;
  8926. this._state = nextState;
  8927. logger["b" /* logger */].log('main stream:' + previousState + '->' + nextState);
  8928. this.hls.trigger(events["a" /* default */].STREAM_STATE_TRANSITION, {
  8929. previousState: previousState,
  8930. nextState: nextState
  8931. });
  8932. }
  8933. },
  8934. get: function get() {
  8935. return this._state;
  8936. }
  8937. }, {
  8938. key: 'currentLevel',
  8939. get: function get() {
  8940. var media = this.media;
  8941. if (media) {
  8942. var frag = this.getBufferedFrag(media.currentTime);
  8943. if (frag) {
  8944. return frag.level;
  8945. }
  8946. }
  8947. return -1;
  8948. }
  8949. }, {
  8950. key: 'nextBufferedFrag',
  8951. get: function get() {
  8952. var media = this.media;
  8953. if (media) {
  8954. // first get end range of current fragment
  8955. return this.followingBufferedFrag(this.getBufferedFrag(media.currentTime));
  8956. } else {
  8957. return null;
  8958. }
  8959. }
  8960. }, {
  8961. key: 'nextLevel',
  8962. get: function get() {
  8963. var frag = this.nextBufferedFrag;
  8964. if (frag) {
  8965. return frag.level;
  8966. } else {
  8967. return -1;
  8968. }
  8969. }
  8970. }, {
  8971. key: 'liveSyncPosition',
  8972. get: function get() {
  8973. return this._liveSyncPosition;
  8974. },
  8975. set: function set(value) {
  8976. this._liveSyncPosition = value;
  8977. }
  8978. }]);
  8979. return StreamController;
  8980. }(task_loop);
  8981. /* harmony default export */
  8982. var stream_controller = (stream_controller_StreamController);
  8983. // CONCATENATED MODULE: ./src/controller/level-controller.js
  8984. var level_controller__createClass = function () {
  8985. function defineProperties(target, props) {
  8986. for (var i = 0; i < props.length; i++) {
  8987. var descriptor = props[i];
  8988. descriptor.enumerable = descriptor.enumerable || false;
  8989. descriptor.configurable = true;
  8990. if ("value" in descriptor) descriptor.writable = true;
  8991. Object.defineProperty(target, descriptor.key, descriptor);
  8992. }
  8993. }
  8994. return function (Constructor, protoProps, staticProps) {
  8995. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  8996. if (staticProps) defineProperties(Constructor, staticProps);
  8997. return Constructor;
  8998. };
  8999. }();
  9000. function level_controller__classCallCheck(instance, Constructor) {
  9001. if (!(instance instanceof Constructor)) {
  9002. throw new TypeError("Cannot call a class as a function");
  9003. }
  9004. }
  9005. function level_controller__possibleConstructorReturn(self, call) {
  9006. if (!self) {
  9007. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  9008. }
  9009. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  9010. }
  9011. function level_controller__inherits(subClass, superClass) {
  9012. if (typeof superClass !== "function" && superClass !== null) {
  9013. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  9014. }
  9015. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9016. constructor: {
  9017. value: subClass,
  9018. enumerable: false,
  9019. writable: true,
  9020. configurable: true
  9021. }
  9022. });
  9023. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  9024. }
  9025. /*
  9026. * Level Controller
  9027. */
  9028. var level_controller_LevelController = function (_EventHandler) {
  9029. level_controller__inherits(LevelController, _EventHandler);
  9030. function LevelController(hls) {
  9031. level_controller__classCallCheck(this, LevelController);
  9032. var _this = level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MANIFEST_LOADED, events["a" /* default */].LEVEL_LOADED, events["a" /* default */].FRAG_LOADED, events["a" /* default */].ERROR));
  9033. _this.canload = false;
  9034. _this.currentLevelIndex = null;
  9035. _this.manualLevelIndex = -1;
  9036. _this.timer = null;
  9037. return _this;
  9038. }
  9039. LevelController.prototype.onHandlerDestroying = function onHandlerDestroying() {
  9040. this.cleanTimer();
  9041. this.manualLevelIndex = -1;
  9042. };
  9043. LevelController.prototype.cleanTimer = function cleanTimer() {
  9044. if (this.timer !== null) {
  9045. clearTimeout(this.timer);
  9046. this.timer = null;
  9047. }
  9048. };
  9049. LevelController.prototype.startLoad = function startLoad() {
  9050. var levels = this._levels;
  9051. this.canload = true;
  9052. this.levelRetryCount = 0;
  9053. // clean up live level details to force reload them, and reset load errors
  9054. if (levels) {
  9055. levels.forEach(function (level) {
  9056. level.loadError = 0;
  9057. var levelDetails = level.details;
  9058. if (levelDetails && levelDetails.live) {
  9059. level.details = undefined;
  9060. }
  9061. });
  9062. }
  9063. // speed up live playlist refresh if timer exists
  9064. if (this.timer !== null) {
  9065. this.loadLevel();
  9066. }
  9067. };
  9068. LevelController.prototype.stopLoad = function stopLoad() {
  9069. this.canload = false;
  9070. };
  9071. LevelController.prototype.onManifestLoaded = function onManifestLoaded(data) {
  9072. var levels = [];
  9073. var bitrateStart = void 0;
  9074. var levelSet = {};
  9075. var levelFromSet = null;
  9076. var videoCodecFound = false;
  9077. var audioCodecFound = false;
  9078. var chromeOrFirefox = /chrome|firefox/.test(navigator.userAgent.toLowerCase());
  9079. var audioTracks = [];
  9080. // regroup redundant levels together
  9081. data.levels.forEach(function (level) {
  9082. level.loadError = 0;
  9083. level.fragmentError = false;
  9084. videoCodecFound = videoCodecFound || !!level.videoCodec;
  9085. audioCodecFound = audioCodecFound || !!level.audioCodec || !!(level.attrs && level.attrs.AUDIO);
  9086. // erase audio codec info if browser does not support mp4a.40.34.
  9087. // demuxer will autodetect codec and fallback to mpeg/audio
  9088. if (chromeOrFirefox === true && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) {
  9089. level.audioCodec = undefined;
  9090. }
  9091. levelFromSet = levelSet[level.bitrate];
  9092. if (levelFromSet === undefined) {
  9093. level.url = [level.url];
  9094. level.urlId = 0;
  9095. levelSet[level.bitrate] = level;
  9096. levels.push(level);
  9097. } else {
  9098. levelFromSet.url.push(level.url);
  9099. }
  9100. });
  9101. // remove audio-only level if we also have levels with audio+video codecs signalled
  9102. if (videoCodecFound === true && audioCodecFound === true) {
  9103. levels = levels.filter(function (_ref) {
  9104. var videoCodec = _ref.videoCodec;
  9105. return !!videoCodec;
  9106. });
  9107. }
  9108. // only keep levels with supported audio/video codecs
  9109. levels = levels.filter(function (_ref2) {
  9110. var audioCodec = _ref2.audioCodec,
  9111. videoCodec = _ref2.videoCodec;
  9112. return (!audioCodec || isCodecSupportedInMp4(audioCodec)) && (!videoCodec || isCodecSupportedInMp4(videoCodec));
  9113. });
  9114. if (data.audioTracks) {
  9115. audioTracks = data.audioTracks.filter(function (track) {
  9116. return !track.audioCodec || isCodecSupportedInMp4(track.audioCodec, 'audio');
  9117. });
  9118. }
  9119. if (levels.length > 0) {
  9120. // start bitrate is the first bitrate of the manifest
  9121. bitrateStart = levels[0].bitrate;
  9122. // sort level on bitrate
  9123. levels.sort(function (a, b) {
  9124. return a.bitrate - b.bitrate;
  9125. });
  9126. this._levels = levels;
  9127. // find index of first level in sorted levels
  9128. for (var i = 0; i < levels.length; i++) {
  9129. if (levels[i].bitrate === bitrateStart) {
  9130. this._firstLevel = i;
  9131. logger["b" /* logger */].log('manifest loaded,' + levels.length + ' level(s) found, first bitrate:' + bitrateStart);
  9132. break;
  9133. }
  9134. }
  9135. this.hls.trigger(events["a" /* default */].MANIFEST_PARSED, {
  9136. levels: levels,
  9137. audioTracks: audioTracks,
  9138. firstLevel: this._firstLevel,
  9139. stats: data.stats,
  9140. audio: audioCodecFound,
  9141. video: videoCodecFound,
  9142. altAudio: audioTracks.length > 0
  9143. });
  9144. } else {
  9145. this.hls.trigger(events["a" /* default */].ERROR, {
  9146. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  9147. details: errors["a" /* ErrorDetails */].MANIFEST_INCOMPATIBLE_CODECS_ERROR,
  9148. fatal: true,
  9149. url: this.hls.url,
  9150. reason: 'no level with compatible codecs found in manifest'
  9151. });
  9152. }
  9153. };
  9154. LevelController.prototype.setLevelInternal = function setLevelInternal(newLevel) {
  9155. var levels = this._levels;
  9156. var hls = this.hls;
  9157. // check if level idx is valid
  9158. if (newLevel >= 0 && newLevel < levels.length) {
  9159. // stopping live reloading timer if any
  9160. this.cleanTimer();
  9161. if (this.currentLevelIndex !== newLevel) {
  9162. logger["b" /* logger */].log('switching to level ' + newLevel);
  9163. this.currentLevelIndex = newLevel;
  9164. var levelProperties = levels[newLevel];
  9165. levelProperties.level = newLevel;
  9166. // LEVEL_SWITCH to be deprecated in next major release
  9167. hls.trigger(events["a" /* default */].LEVEL_SWITCH, levelProperties);
  9168. hls.trigger(events["a" /* default */].LEVEL_SWITCHING, levelProperties);
  9169. }
  9170. var level = levels[newLevel],
  9171. levelDetails = level.details;
  9172. // check if we need to load playlist for this level
  9173. if (!levelDetails || levelDetails.live === true) {
  9174. // level not retrieved yet, or live playlist we need to (re)load it
  9175. var urlId = level.urlId;
  9176. hls.trigger(events["a" /* default */].LEVEL_LOADING, {
  9177. url: level.url[urlId],
  9178. level: newLevel,
  9179. id: urlId
  9180. });
  9181. }
  9182. } else {
  9183. // invalid level id given, trigger error
  9184. hls.trigger(events["a" /* default */].ERROR, {
  9185. type: errors["b" /* ErrorTypes */].OTHER_ERROR,
  9186. details: errors["a" /* ErrorDetails */].LEVEL_SWITCH_ERROR,
  9187. level: newLevel,
  9188. fatal: false,
  9189. reason: 'invalid level idx'
  9190. });
  9191. }
  9192. };
  9193. LevelController.prototype.onError = function onError(data) {
  9194. if (data.fatal === true) {
  9195. if (data.type === errors["b" /* ErrorTypes */].NETWORK_ERROR) {
  9196. this.cleanTimer();
  9197. }
  9198. return;
  9199. }
  9200. var levelError = false,
  9201. fragmentError = false;
  9202. var levelIndex = void 0;
  9203. // try to recover not fatal errors
  9204. switch (data.details) {
  9205. case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
  9206. case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
  9207. case errors["a" /* ErrorDetails */].FRAG_LOOP_LOADING_ERROR:
  9208. case errors["a" /* ErrorDetails */].KEY_LOAD_ERROR:
  9209. case errors["a" /* ErrorDetails */].KEY_LOAD_TIMEOUT:
  9210. levelIndex = data.frag.level;
  9211. fragmentError = true;
  9212. break;
  9213. case errors["a" /* ErrorDetails */].LEVEL_LOAD_ERROR:
  9214. case errors["a" /* ErrorDetails */].LEVEL_LOAD_TIMEOUT:
  9215. levelIndex = data.context.level;
  9216. levelError = true;
  9217. break;
  9218. case errors["a" /* ErrorDetails */].REMUX_ALLOC_ERROR:
  9219. levelIndex = data.level;
  9220. levelError = true;
  9221. break;
  9222. }
  9223. if (levelIndex !== undefined) {
  9224. this.recoverLevel(data, levelIndex, levelError, fragmentError);
  9225. }
  9226. };
  9227. /**
  9228. * Switch to a redundant stream if any available.
  9229. * If redundant stream is not available, emergency switch down if ABR mode is enabled.
  9230. *
  9231. * @param {Object} errorEvent
  9232. * @param {Number} levelIndex current level index
  9233. * @param {Boolean} levelError
  9234. * @param {Boolean} fragmentError
  9235. */
  9236. // FIXME Find a better abstraction where fragment/level retry management is well decoupled
  9237. LevelController.prototype.recoverLevel = function recoverLevel(errorEvent, levelIndex, levelError, fragmentError) {
  9238. var _this2 = this;
  9239. var config = this.hls.config;
  9240. var errorDetails = errorEvent.details;
  9241. var level = this._levels[levelIndex];
  9242. var redundantLevels = void 0,
  9243. delay = void 0,
  9244. nextLevel = void 0;
  9245. level.loadError++;
  9246. level.fragmentError = fragmentError;
  9247. if (levelError === true) {
  9248. if (this.levelRetryCount + 1 <= config.levelLoadingMaxRetry) {
  9249. // exponential backoff capped to max retry timeout
  9250. delay = Math.min(Math.pow(2, this.levelRetryCount) * config.levelLoadingRetryDelay, config.levelLoadingMaxRetryTimeout);
  9251. // Schedule level reload
  9252. this.timer = setTimeout(function () {
  9253. return _this2.loadLevel();
  9254. }, delay);
  9255. // boolean used to inform stream controller not to switch back to IDLE on non fatal error
  9256. errorEvent.levelRetry = true;
  9257. this.levelRetryCount++;
  9258. logger["b" /* logger */].warn('level controller, ' + errorDetails + ', retry in ' + delay + ' ms, current retry count is ' + this.levelRetryCount);
  9259. } else {
  9260. logger["b" /* logger */].error('level controller, cannot recover from ' + errorDetails + ' error');
  9261. this.currentLevelIndex = null;
  9262. // stopping live reloading timer if any
  9263. this.cleanTimer();
  9264. // switch error to fatal
  9265. errorEvent.fatal = true;
  9266. return;
  9267. }
  9268. }
  9269. // Try any redundant streams if available for both errors: level and fragment
  9270. // If level.loadError reaches redundantLevels it means that we tried them all, no hope => let's switch down
  9271. if (levelError === true || fragmentError === true) {
  9272. redundantLevels = level.url.length;
  9273. if (redundantLevels > 1 && level.loadError < redundantLevels) {
  9274. logger["b" /* logger */].warn('level controller, ' + errorDetails + ' for level ' + levelIndex + ': switching to redundant stream id ' + level.urlId);
  9275. level.urlId = (level.urlId + 1) % redundantLevels;
  9276. level.details = undefined;
  9277. } else {
  9278. // Search for available level
  9279. if (this.manualLevelIndex === -1) {
  9280. // When lowest level has been reached, let's start hunt from the top
  9281. nextLevel = levelIndex === 0 ? this._levels.length - 1 : levelIndex - 1;
  9282. logger["b" /* logger */].warn('level controller, ' + errorDetails + ': switch to ' + nextLevel);
  9283. this.hls.nextAutoLevel = this.currentLevelIndex = nextLevel;
  9284. } else if (fragmentError === true) {
  9285. // Allow fragment retry as long as configuration allows.
  9286. // reset this._level so that another call to set level() will trigger again a frag load
  9287. logger["b" /* logger */].warn('level controller, ' + errorDetails + ': reload a fragment');
  9288. this.currentLevelIndex = null;
  9289. }
  9290. }
  9291. }
  9292. };
  9293. // reset errors on the successful load of a fragment
  9294. LevelController.prototype.onFragLoaded = function onFragLoaded(_ref3) {
  9295. var frag = _ref3.frag;
  9296. if (frag !== undefined && frag.type === 'main') {
  9297. var level = this._levels[frag.level];
  9298. if (level !== undefined) {
  9299. level.fragmentError = false;
  9300. level.loadError = 0;
  9301. this.levelRetryCount = 0;
  9302. }
  9303. }
  9304. };
  9305. LevelController.prototype.onLevelLoaded = function onLevelLoaded(data) {
  9306. var _this3 = this;
  9307. var levelId = data.level;
  9308. // only process level loaded events matching with expected level
  9309. if (levelId === this.currentLevelIndex) {
  9310. var curLevel = this._levels[levelId];
  9311. // reset level load error counter on successful level loaded only if there is no issues with fragments
  9312. if (curLevel.fragmentError === false) {
  9313. curLevel.loadError = 0;
  9314. this.levelRetryCount = 0;
  9315. }
  9316. var newDetails = data.details;
  9317. // if current playlist is a live playlist, arm a timer to reload it
  9318. if (newDetails.live) {
  9319. var reloadInterval = 1000 * (newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration),
  9320. curDetails = curLevel.details;
  9321. if (curDetails && newDetails.endSN === curDetails.endSN) {
  9322. // follow HLS Spec, If the client reloads a Playlist file and finds that it has not
  9323. // changed then it MUST wait for a period of one-half the target
  9324. // duration before retrying.
  9325. reloadInterval /= 2;
  9326. logger["b" /* logger */].log('same live playlist, reload twice faster');
  9327. }
  9328. // decrement reloadInterval with level loading delay
  9329. reloadInterval -= performance.now() - data.stats.trequest;
  9330. // in any case, don't reload more than every second
  9331. reloadInterval = Math.max(1000, Math.round(reloadInterval));
  9332. logger["b" /* logger */].log('live playlist, reload in ' + reloadInterval + ' ms');
  9333. this.timer = setTimeout(function () {
  9334. return _this3.loadLevel();
  9335. }, reloadInterval);
  9336. } else {
  9337. this.cleanTimer();
  9338. }
  9339. }
  9340. };
  9341. LevelController.prototype.loadLevel = function loadLevel() {
  9342. var level = void 0,
  9343. urlIndex = void 0;
  9344. if (this.currentLevelIndex !== null && this.canload === true) {
  9345. level = this._levels[this.currentLevelIndex];
  9346. if (level !== undefined && level.url.length > 0) {
  9347. urlIndex = level.urlId;
  9348. this.hls.trigger(events["a" /* default */].LEVEL_LOADING, {
  9349. url: level.url[urlIndex],
  9350. level: this.currentLevelIndex,
  9351. id: urlIndex
  9352. });
  9353. }
  9354. }
  9355. };
  9356. level_controller__createClass(LevelController, [{
  9357. key: 'levels',
  9358. get: function get() {
  9359. return this._levels;
  9360. }
  9361. }, {
  9362. key: 'level',
  9363. get: function get() {
  9364. return this.currentLevelIndex;
  9365. },
  9366. set: function set(newLevel) {
  9367. var levels = this._levels;
  9368. if (levels) {
  9369. newLevel = Math.min(newLevel, levels.length - 1);
  9370. if (this.currentLevelIndex !== newLevel || levels[newLevel].details === undefined) {
  9371. this.setLevelInternal(newLevel);
  9372. }
  9373. }
  9374. }
  9375. }, {
  9376. key: 'manualLevel',
  9377. get: function get() {
  9378. return this.manualLevelIndex;
  9379. },
  9380. set: function set(newLevel) {
  9381. this.manualLevelIndex = newLevel;
  9382. if (this._startLevel === undefined) {
  9383. this._startLevel = newLevel;
  9384. }
  9385. if (newLevel !== -1) {
  9386. this.level = newLevel;
  9387. }
  9388. }
  9389. }, {
  9390. key: 'firstLevel',
  9391. get: function get() {
  9392. return this._firstLevel;
  9393. },
  9394. set: function set(newLevel) {
  9395. this._firstLevel = newLevel;
  9396. }
  9397. }, {
  9398. key: 'startLevel',
  9399. get: function get() {
  9400. // hls.startLevel takes precedence over config.startLevel
  9401. // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest)
  9402. if (this._startLevel === undefined) {
  9403. var configStartLevel = this.hls.config.startLevel;
  9404. if (configStartLevel !== undefined) {
  9405. return configStartLevel;
  9406. } else {
  9407. return this._firstLevel;
  9408. }
  9409. } else {
  9410. return this._startLevel;
  9411. }
  9412. },
  9413. set: function set(newLevel) {
  9414. this._startLevel = newLevel;
  9415. }
  9416. }, {
  9417. key: 'nextLoadLevel',
  9418. get: function get() {
  9419. if (this.manualLevelIndex !== -1) {
  9420. return this.manualLevelIndex;
  9421. } else {
  9422. return this.hls.nextAutoLevel;
  9423. }
  9424. },
  9425. set: function set(nextLevel) {
  9426. this.level = nextLevel;
  9427. if (this.manualLevelIndex === -1) {
  9428. this.hls.nextAutoLevel = nextLevel;
  9429. }
  9430. }
  9431. }]);
  9432. return LevelController;
  9433. }(event_handler);
  9434. /* harmony default export */
  9435. var level_controller = (level_controller_LevelController);
  9436. // EXTERNAL MODULE: ./src/demux/id3.js
  9437. var id3 = __webpack_require__(4);
  9438. // CONCATENATED MODULE: ./src/controller/id3-track-controller.js
  9439. function id3_track_controller__classCallCheck(instance, Constructor) {
  9440. if (!(instance instanceof Constructor)) {
  9441. throw new TypeError("Cannot call a class as a function");
  9442. }
  9443. }
  9444. function id3_track_controller__possibleConstructorReturn(self, call) {
  9445. if (!self) {
  9446. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  9447. }
  9448. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  9449. }
  9450. function id3_track_controller__inherits(subClass, superClass) {
  9451. if (typeof superClass !== "function" && superClass !== null) {
  9452. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  9453. }
  9454. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9455. constructor: {
  9456. value: subClass,
  9457. enumerable: false,
  9458. writable: true,
  9459. configurable: true
  9460. }
  9461. });
  9462. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  9463. }
  9464. /*
  9465. * id3 metadata track controller
  9466. */
  9467. var id3_track_controller_ID3TrackController = function (_EventHandler) {
  9468. id3_track_controller__inherits(ID3TrackController, _EventHandler);
  9469. function ID3TrackController(hls) {
  9470. id3_track_controller__classCallCheck(this, ID3TrackController);
  9471. var _this = id3_track_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].FRAG_PARSING_METADATA));
  9472. _this.id3Track = undefined;
  9473. _this.media = undefined;
  9474. return _this;
  9475. }
  9476. ID3TrackController.prototype.destroy = function destroy() {
  9477. event_handler.prototype.destroy.call(this);
  9478. };
  9479. // Add ID3 metatadata text track.
  9480. ID3TrackController.prototype.onMediaAttached = function onMediaAttached(data) {
  9481. this.media = data.media;
  9482. if (!this.media) {
  9483. return;
  9484. }
  9485. };
  9486. ID3TrackController.prototype.onMediaDetaching = function onMediaDetaching() {
  9487. this.media = undefined;
  9488. };
  9489. ID3TrackController.prototype.onFragParsingMetadata = function onFragParsingMetadata(data) {
  9490. var fragment = data.frag;
  9491. var samples = data.samples;
  9492. // create track dynamically
  9493. if (!this.id3Track) {
  9494. this.id3Track = this.media.addTextTrack('metadata', 'id3');
  9495. this.id3Track.mode = 'hidden';
  9496. }
  9497. // Attempt to recreate Safari functionality by creating
  9498. // WebKitDataCue objects when available and store the decoded
  9499. // ID3 data in the value property of the cue
  9500. var Cue = window.WebKitDataCue || window.VTTCue || window.TextTrackCue;
  9501. for (var i = 0; i < samples.length; i++) {
  9502. var frames = id3["a" /* default */].getID3Frames(samples[i].data);
  9503. if (frames) {
  9504. var startTime = samples[i].pts;
  9505. var endTime = i < samples.length - 1 ? samples[i + 1].pts : fragment.endPTS;
  9506. // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
  9507. if (startTime === endTime) {
  9508. endTime += 0.0001;
  9509. }
  9510. for (var j = 0; j < frames.length; j++) {
  9511. var frame = frames[j];
  9512. // Safari doesn't put the timestamp frame in the TextTrack
  9513. if (!id3["a" /* default */].isTimeStampFrame(frame)) {
  9514. var cue = new Cue(startTime, endTime, '');
  9515. cue.value = frame;
  9516. this.id3Track.addCue(cue);
  9517. }
  9518. }
  9519. }
  9520. }
  9521. };
  9522. return ID3TrackController;
  9523. }(event_handler);
  9524. /* harmony default export */
  9525. var id3_track_controller = (id3_track_controller_ID3TrackController);
  9526. // CONCATENATED MODULE: ./src/helper/is-supported.js
  9527. function is_supported_isSupported() {
  9528. var mediaSource = getMediaSource();
  9529. var sourceBuffer = window.SourceBuffer || window.WebKitSourceBuffer;
  9530. var isTypeSupported = mediaSource && typeof mediaSource.isTypeSupported === 'function' && mediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
  9531. // if SourceBuffer is exposed ensure its API is valid
  9532. // safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible
  9533. var sourceBufferValidAPI = !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function';
  9534. return !!isTypeSupported && !!sourceBufferValidAPI;
  9535. }
  9536. // CONCATENATED MODULE: ./src/utils/ewma.js
  9537. function ewma__classCallCheck(instance, Constructor) {
  9538. if (!(instance instanceof Constructor)) {
  9539. throw new TypeError("Cannot call a class as a function");
  9540. }
  9541. }
  9542. /*
  9543. * compute an Exponential Weighted moving average
  9544. * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  9545. * - heavily inspired from shaka-player
  9546. */
  9547. var EWMA = function () {
  9548. // About half of the estimated value will be from the last |halfLife| samples by weight.
  9549. function EWMA(halfLife) {
  9550. ewma__classCallCheck(this, EWMA);
  9551. // Larger values of alpha expire historical data more slowly.
  9552. this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;
  9553. this.estimate_ = 0;
  9554. this.totalWeight_ = 0;
  9555. }
  9556. EWMA.prototype.sample = function sample(weight, value) {
  9557. var adjAlpha = Math.pow(this.alpha_, weight);
  9558. this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;
  9559. this.totalWeight_ += weight;
  9560. };
  9561. EWMA.prototype.getTotalWeight = function getTotalWeight() {
  9562. return this.totalWeight_;
  9563. };
  9564. EWMA.prototype.getEstimate = function getEstimate() {
  9565. if (this.alpha_) {
  9566. var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);
  9567. return this.estimate_ / zeroFactor;
  9568. } else {
  9569. return this.estimate_;
  9570. }
  9571. };
  9572. return EWMA;
  9573. }();
  9574. /* harmony default export */
  9575. var ewma = (EWMA);
  9576. // CONCATENATED MODULE: ./src/utils/ewma-bandwidth-estimator.js
  9577. function ewma_bandwidth_estimator__classCallCheck(instance, Constructor) {
  9578. if (!(instance instanceof Constructor)) {
  9579. throw new TypeError("Cannot call a class as a function");
  9580. }
  9581. }
  9582. /*
  9583. * EWMA Bandwidth Estimator
  9584. * - heavily inspired from shaka-player
  9585. * Tracks bandwidth samples and estimates available bandwidth.
  9586. * Based on the minimum of two exponentially-weighted moving averages with
  9587. * different half-lives.
  9588. */
  9589. var ewma_bandwidth_estimator_EwmaBandWidthEstimator = function () {
  9590. function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) {
  9591. ewma_bandwidth_estimator__classCallCheck(this, EwmaBandWidthEstimator);
  9592. this.hls = hls;
  9593. this.defaultEstimate_ = defaultEstimate;
  9594. this.minWeight_ = 0.001;
  9595. this.minDelayMs_ = 50;
  9596. this.slow_ = new ewma(slow);
  9597. this.fast_ = new ewma(fast);
  9598. }
  9599. EwmaBandWidthEstimator.prototype.sample = function sample(durationMs, numBytes) {
  9600. durationMs = Math.max(durationMs, this.minDelayMs_);
  9601. var bandwidth = 8000 * numBytes / durationMs,
  9602. //console.log('instant bw:'+ Math.round(bandwidth));
  9603. // we weight sample using loading duration....
  9604. weight = durationMs / 1000;
  9605. this.fast_.sample(weight, bandwidth);
  9606. this.slow_.sample(weight, bandwidth);
  9607. };
  9608. EwmaBandWidthEstimator.prototype.canEstimate = function canEstimate() {
  9609. var fast = this.fast_;
  9610. return fast && fast.getTotalWeight() >= this.minWeight_;
  9611. };
  9612. EwmaBandWidthEstimator.prototype.getEstimate = function getEstimate() {
  9613. if (this.canEstimate()) {
  9614. //console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
  9615. //console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));
  9616. // Take the minimum of these two estimates. This should have the effect of
  9617. // adapting down quickly, but up more slowly.
  9618. return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate());
  9619. } else {
  9620. return this.defaultEstimate_;
  9621. }
  9622. };
  9623. EwmaBandWidthEstimator.prototype.destroy = function destroy() {
  9624. };
  9625. return EwmaBandWidthEstimator;
  9626. }();
  9627. /* harmony default export */
  9628. var ewma_bandwidth_estimator = (ewma_bandwidth_estimator_EwmaBandWidthEstimator);
  9629. // CONCATENATED MODULE: ./src/controller/abr-controller.js
  9630. var abr_controller__createClass = function () {
  9631. function defineProperties(target, props) {
  9632. for (var i = 0; i < props.length; i++) {
  9633. var descriptor = props[i];
  9634. descriptor.enumerable = descriptor.enumerable || false;
  9635. descriptor.configurable = true;
  9636. if ("value" in descriptor) descriptor.writable = true;
  9637. Object.defineProperty(target, descriptor.key, descriptor);
  9638. }
  9639. }
  9640. return function (Constructor, protoProps, staticProps) {
  9641. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  9642. if (staticProps) defineProperties(Constructor, staticProps);
  9643. return Constructor;
  9644. };
  9645. }();
  9646. function abr_controller__classCallCheck(instance, Constructor) {
  9647. if (!(instance instanceof Constructor)) {
  9648. throw new TypeError("Cannot call a class as a function");
  9649. }
  9650. }
  9651. function abr_controller__possibleConstructorReturn(self, call) {
  9652. if (!self) {
  9653. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  9654. }
  9655. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  9656. }
  9657. function abr_controller__inherits(subClass, superClass) {
  9658. if (typeof superClass !== "function" && superClass !== null) {
  9659. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  9660. }
  9661. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9662. constructor: {
  9663. value: subClass,
  9664. enumerable: false,
  9665. writable: true,
  9666. configurable: true
  9667. }
  9668. });
  9669. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  9670. }
  9671. /*
  9672. * simple ABR Controller
  9673. * - compute next level based on last fragment bw heuristics
  9674. * - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling
  9675. */
  9676. var abr_controller_AbrController = function (_EventHandler) {
  9677. abr_controller__inherits(AbrController, _EventHandler);
  9678. function AbrController(hls) {
  9679. abr_controller__classCallCheck(this, AbrController);
  9680. var _this = abr_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FRAG_LOADING, events["a" /* default */].FRAG_LOADED, events["a" /* default */].FRAG_BUFFERED, events["a" /* default */].ERROR));
  9681. _this.lastLoadedFragLevel = 0;
  9682. _this._nextAutoLevel = -1;
  9683. _this.hls = hls;
  9684. _this.timer = null;
  9685. _this._bwEstimator = null;
  9686. _this.onCheck = _this._abandonRulesCheck.bind(_this);
  9687. return _this;
  9688. }
  9689. AbrController.prototype.destroy = function destroy() {
  9690. this.clearTimer();
  9691. event_handler.prototype.destroy.call(this);
  9692. };
  9693. AbrController.prototype.onFragLoading = function onFragLoading(data) {
  9694. var frag = data.frag;
  9695. if (frag.type === 'main') {
  9696. if (!this.timer) {
  9697. this.timer = setInterval(this.onCheck, 100);
  9698. }
  9699. // lazy init of bw Estimator, rationale is that we use different params for Live/VoD
  9700. // so we need to wait for stream manifest / playlist type to instantiate it.
  9701. if (!this._bwEstimator) {
  9702. var hls = this.hls,
  9703. level = data.frag.level,
  9704. isLive = hls.levels[level].details.live,
  9705. config = hls.config,
  9706. ewmaFast = void 0,
  9707. ewmaSlow = void 0;
  9708. if (isLive) {
  9709. ewmaFast = config.abrEwmaFastLive;
  9710. ewmaSlow = config.abrEwmaSlowLive;
  9711. } else {
  9712. ewmaFast = config.abrEwmaFastVoD;
  9713. ewmaSlow = config.abrEwmaSlowVoD;
  9714. }
  9715. this._bwEstimator = new ewma_bandwidth_estimator(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
  9716. }
  9717. this.fragCurrent = frag;
  9718. }
  9719. };
  9720. AbrController.prototype._abandonRulesCheck = function _abandonRulesCheck() {
  9721. /*
  9722. monitor fragment retrieval time...
  9723. we compute expected time of arrival of the complete fragment.
  9724. we compare it to expected time of buffer starvation
  9725. */
  9726. var hls = this.hls,
  9727. v = hls.media,
  9728. frag = this.fragCurrent,
  9729. loader = frag.loader,
  9730. minAutoLevel = hls.minAutoLevel;
  9731. // if loader has been destroyed or loading has been aborted, stop timer and return
  9732. if (!loader || loader.stats && loader.stats.aborted) {
  9733. logger["b" /* logger */].warn('frag loader destroy or aborted, disarm abandonRules');
  9734. this.clearTimer();
  9735. // reset forced auto level value so that next level will be selected
  9736. this._nextAutoLevel = -1;
  9737. return;
  9738. }
  9739. var stats = loader.stats;
  9740. /* only monitor frag retrieval time if
  9741. (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */
  9742. if (v && stats && (!v.paused && v.playbackRate !== 0 || !v.readyState) && frag.autoLevel && frag.level) {
  9743. var requestDelay = performance.now() - stats.trequest,
  9744. playbackRate = Math.abs(v.playbackRate);
  9745. // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
  9746. if (requestDelay > 500 * frag.duration / playbackRate) {
  9747. var levels = hls.levels,
  9748. loadRate = Math.max(1, stats.bw ? stats.bw / 8 : stats.loaded * 1000 / requestDelay),
  9749. // byte/s; at least 1 byte/s to avoid division by zero
  9750. // compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size
  9751. level = levels[frag.level],
  9752. levelBitrate = level.realBitrate ? Math.max(level.realBitrate, level.bitrate) : level.bitrate,
  9753. expectedLen = stats.total ? stats.total : Math.max(stats.loaded, Math.round(frag.duration * levelBitrate / 8)),
  9754. pos = v.currentTime,
  9755. fragLoadedDelay = (expectedLen - stats.loaded) / loadRate,
  9756. bufferStarvationDelay = (buffer_helper.bufferInfo(v, pos, hls.config.maxBufferHole).end - pos) / playbackRate;
  9757. // consider emergency switch down only if we have less than 2 frag buffered AND
  9758. // time to finish loading current fragment is bigger than buffer starvation delay
  9759. // ie if we risk buffer starvation if bw does not increase quickly
  9760. if (bufferStarvationDelay < 2 * frag.duration / playbackRate && fragLoadedDelay > bufferStarvationDelay) {
  9761. var fragLevelNextLoadedDelay = void 0,
  9762. nextLoadLevel = void 0;
  9763. // lets iterate through lower level and try to find the biggest one that could avoid rebuffering
  9764. // we start from current level - 1 and we step down , until we find a matching level
  9765. for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) {
  9766. // compute time to load next fragment at lower level
  9767. // 0.8 : consider only 80% of current bw to be conservative
  9768. // 8 = bits per byte (bps/Bps)
  9769. var levelNextBitrate = levels[nextLoadLevel].realBitrate ? Math.max(levels[nextLoadLevel].realBitrate, levels[nextLoadLevel].bitrate) : levels[nextLoadLevel].bitrate;
  9770. fragLevelNextLoadedDelay = frag.duration * levelNextBitrate / (8 * 0.8 * loadRate);
  9771. if (fragLevelNextLoadedDelay < bufferStarvationDelay) {
  9772. // we found a lower level that be rebuffering free with current estimated bw !
  9773. break;
  9774. }
  9775. }
  9776. // only emergency switch down if it takes less time to load new fragment at lowest level instead
  9777. // of finishing loading current one ...
  9778. if (fragLevelNextLoadedDelay < fragLoadedDelay) {
  9779. logger["b" /* logger */].warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel + ':fragLoadedDelay[' + nextLoadLevel + ']<fragLoadedDelay[' + (frag.level - 1) + '];bufferStarvationDelay:' + fragLevelNextLoadedDelay.toFixed(1) + '<' + fragLoadedDelay.toFixed(1) + ':' + bufferStarvationDelay.toFixed(1));
  9780. // force next load level in auto mode
  9781. hls.nextLoadLevel = nextLoadLevel;
  9782. // update bw estimate for this fragment before cancelling load (this will help reducing the bw)
  9783. this._bwEstimator.sample(requestDelay, stats.loaded);
  9784. //abort fragment loading
  9785. loader.abort();
  9786. // stop abandon rules timer
  9787. this.clearTimer();
  9788. hls.trigger(events["a" /* default */].FRAG_LOAD_EMERGENCY_ABORTED, {
  9789. frag: frag,
  9790. stats: stats
  9791. });
  9792. }
  9793. }
  9794. }
  9795. }
  9796. };
  9797. AbrController.prototype.onFragLoaded = function onFragLoaded(data) {
  9798. var frag = data.frag;
  9799. if (frag.type === 'main' && !isNaN(frag.sn)) {
  9800. // stop monitoring bw once frag loaded
  9801. this.clearTimer();
  9802. // store level id after successful fragment load
  9803. this.lastLoadedFragLevel = frag.level;
  9804. // reset forced auto level value so that next level will be selected
  9805. this._nextAutoLevel = -1;
  9806. // compute level average bitrate
  9807. if (this.hls.config.abrMaxWithRealBitrate) {
  9808. var level = this.hls.levels[frag.level];
  9809. var loadedBytes = (level.loaded ? level.loaded.bytes : 0) + data.stats.loaded;
  9810. var loadedDuration = (level.loaded ? level.loaded.duration : 0) + data.frag.duration;
  9811. level.loaded = {bytes: loadedBytes, duration: loadedDuration};
  9812. level.realBitrate = Math.round(8 * loadedBytes / loadedDuration);
  9813. }
  9814. // if fragment has been loaded to perform a bitrate test,
  9815. if (data.frag.bitrateTest) {
  9816. var stats = data.stats;
  9817. stats.tparsed = stats.tbuffered = stats.tload;
  9818. this.onFragBuffered(data);
  9819. }
  9820. }
  9821. };
  9822. AbrController.prototype.onFragBuffered = function onFragBuffered(data) {
  9823. var stats = data.stats,
  9824. frag = data.frag;
  9825. // only update stats on first frag buffering
  9826. // if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
  9827. // and leading to wrong bw estimation
  9828. // on bitrate test, also only update stats once (if tload = tbuffered == on FRAG_LOADED)
  9829. if (stats.aborted !== true && frag.loadCounter === 1 && frag.type === 'main' && !isNaN(frag.sn) && (!frag.bitrateTest || stats.tload === stats.tbuffered)) {
  9830. // use tparsed-trequest instead of tbuffered-trequest to compute fragLoadingProcessing; rationale is that buffer appending only happens once media is attached
  9831. // in case we use config.startFragPrefetch while media is not attached yet, fragment might be parsed while media not attached yet, but it will only be buffered on media attached
  9832. // as a consequence it could happen really late in the process. meaning that appending duration might appears huge ... leading to underestimated throughput estimation
  9833. var fragLoadingProcessingMs = stats.tparsed - stats.trequest;
  9834. logger["b" /* logger */].log('latency/loading/parsing/append/kbps:' + Math.round(stats.tfirst - stats.trequest) + '/' + Math.round(stats.tload - stats.tfirst) + '/' + Math.round(stats.tparsed - stats.tload) + '/' + Math.round(stats.tbuffered - stats.tparsed) + '/' + Math.round(8 * stats.loaded / (stats.tbuffered - stats.trequest)));
  9835. this._bwEstimator.sample(fragLoadingProcessingMs, stats.loaded);
  9836. stats.bwEstimate = this._bwEstimator.getEstimate();
  9837. // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration
  9838. if (frag.bitrateTest) {
  9839. this.bitrateTestDelay = fragLoadingProcessingMs / 1000;
  9840. } else {
  9841. this.bitrateTestDelay = 0;
  9842. }
  9843. }
  9844. };
  9845. AbrController.prototype.onError = function onError(data) {
  9846. // stop timer in case of frag loading error
  9847. switch (data.details) {
  9848. case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
  9849. case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
  9850. this.clearTimer();
  9851. break;
  9852. default:
  9853. break;
  9854. }
  9855. };
  9856. AbrController.prototype.clearTimer = function clearTimer() {
  9857. clearInterval(this.timer);
  9858. this.timer = null;
  9859. };
  9860. // return next auto level
  9861. AbrController.prototype._findBestLevel = function _findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) {
  9862. for (var i = maxAutoLevel; i >= minAutoLevel; i--) {
  9863. var levelInfo = levels[i],
  9864. levelDetails = levelInfo.details,
  9865. avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration,
  9866. live = levelDetails ? levelDetails.live : false,
  9867. adjustedbw = void 0;
  9868. // follow algorithm captured from stagefright :
  9869. // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
  9870. // Pick the highest bandwidth stream below or equal to estimated bandwidth.
  9871. // consider only 80% of the available bandwidth, but if we are switching up,
  9872. // be even more conservative (70%) to avoid overestimating and immediately
  9873. // switching back.
  9874. if (i <= currentLevel) {
  9875. adjustedbw = bwFactor * currentBw;
  9876. } else {
  9877. adjustedbw = bwUpFactor * currentBw;
  9878. }
  9879. var bitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate,
  9880. fetchDuration = bitrate * avgDuration / adjustedbw;
  9881. logger["b" /* logger */].trace('level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: ' + i + '/' + Math.round(adjustedbw) + '/' + bitrate + '/' + avgDuration + '/' + maxFetchDuration + '/' + fetchDuration);
  9882. // if adjusted bw is greater than level bitrate AND
  9883. if (adjustedbw > bitrate && (
  9884. // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches
  9885. // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ...
  9886. // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that _findBestLevel will return -1
  9887. !fetchDuration || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration)) {
  9888. // as we are looping from highest to lowest, this will return the best achievable quality level
  9889. return i;
  9890. }
  9891. }
  9892. // not enough time budget even with quality level 0 ... rebuffering might happen
  9893. return -1;
  9894. };
  9895. abr_controller__createClass(AbrController, [{
  9896. key: 'nextAutoLevel',
  9897. get: function get() {
  9898. var forcedAutoLevel = this._nextAutoLevel;
  9899. var bwEstimator = this._bwEstimator;
  9900. // in case next auto level has been forced, and bw not available or not reliable, return forced value
  9901. if (forcedAutoLevel !== -1 && (!bwEstimator || !bwEstimator.canEstimate())) {
  9902. return forcedAutoLevel;
  9903. }
  9904. // compute next level using ABR logic
  9905. var nextABRAutoLevel = this._nextABRAutoLevel;
  9906. // if forced auto level has been defined, use it to cap ABR computed quality level
  9907. if (forcedAutoLevel !== -1) {
  9908. nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel);
  9909. }
  9910. return nextABRAutoLevel;
  9911. },
  9912. set: function set(nextLevel) {
  9913. this._nextAutoLevel = nextLevel;
  9914. }
  9915. }, {
  9916. key: '_nextABRAutoLevel',
  9917. get: function get() {
  9918. var hls = this.hls,
  9919. maxAutoLevel = hls.maxAutoLevel,
  9920. levels = hls.levels,
  9921. config = hls.config,
  9922. minAutoLevel = hls.minAutoLevel;
  9923. var v = hls.media,
  9924. currentLevel = this.lastLoadedFragLevel,
  9925. currentFragDuration = this.fragCurrent ? this.fragCurrent.duration : 0,
  9926. pos = v ? v.currentTime : 0,
  9927. // playbackRate is the absolute value of the playback rate; if v.playbackRate is 0, we use 1 to load as
  9928. // if we're playing back at the normal rate.
  9929. playbackRate = v && v.playbackRate !== 0 ? Math.abs(v.playbackRate) : 1.0,
  9930. avgbw = this._bwEstimator ? this._bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,
  9931. // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted.
  9932. bufferStarvationDelay = (buffer_helper.bufferInfo(v, pos, config.maxBufferHole).end - pos) / playbackRate;
  9933. // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all
  9934. var bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor, levels);
  9935. if (bestLevel >= 0) {
  9936. return bestLevel;
  9937. } else {
  9938. logger["b" /* logger */].trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering');
  9939. // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering
  9940. // if no matching level found, logic will return 0
  9941. var maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay,
  9942. bwFactor = config.abrBandWidthFactor,
  9943. bwUpFactor = config.abrBandWidthUpFactor;
  9944. if (bufferStarvationDelay === 0) {
  9945. // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test
  9946. var bitrateTestDelay = this.bitrateTestDelay;
  9947. if (bitrateTestDelay) {
  9948. // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value
  9949. // max video loading delay used in automatic start level selection :
  9950. // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level +
  9951. // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` )
  9952. // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
  9953. var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
  9954. maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
  9955. logger["b" /* logger */].trace('bitrate test took ' + Math.round(1000 * bitrateTestDelay) + 'ms, set first fragment max fetchDuration to ' + Math.round(1000 * maxStarvationDelay) + ' ms');
  9956. // don't use conservative factor on bitrate test
  9957. bwFactor = bwUpFactor = 1;
  9958. }
  9959. }
  9960. bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor, levels);
  9961. return Math.max(bestLevel, 0);
  9962. }
  9963. }
  9964. }]);
  9965. return AbrController;
  9966. }(event_handler);
  9967. /* harmony default export */
  9968. var abr_controller = (abr_controller_AbrController);
  9969. // CONCATENATED MODULE: ./src/controller/buffer-controller.js
  9970. function buffer_controller__classCallCheck(instance, Constructor) {
  9971. if (!(instance instanceof Constructor)) {
  9972. throw new TypeError("Cannot call a class as a function");
  9973. }
  9974. }
  9975. function buffer_controller__possibleConstructorReturn(self, call) {
  9976. if (!self) {
  9977. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  9978. }
  9979. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  9980. }
  9981. function buffer_controller__inherits(subClass, superClass) {
  9982. if (typeof superClass !== "function" && superClass !== null) {
  9983. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  9984. }
  9985. subClass.prototype = Object.create(superClass && superClass.prototype, {
  9986. constructor: {
  9987. value: subClass,
  9988. enumerable: false,
  9989. writable: true,
  9990. configurable: true
  9991. }
  9992. });
  9993. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  9994. }
  9995. /*
  9996. * Buffer Controller
  9997. */
  9998. var buffer_controller_MediaSource = getMediaSource();
  9999. var buffer_controller_BufferController = function (_EventHandler) {
  10000. buffer_controller__inherits(BufferController, _EventHandler);
  10001. function BufferController(hls) {
  10002. buffer_controller__classCallCheck(this, BufferController);
  10003. // the value that we have set mediasource.duration to
  10004. // (the actual duration may be tweaked slighly by the browser)
  10005. var _this = buffer_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHING, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].MANIFEST_PARSED, events["a" /* default */].BUFFER_RESET, events["a" /* default */].BUFFER_APPENDING, events["a" /* default */].BUFFER_CODECS, events["a" /* default */].BUFFER_EOS, events["a" /* default */].BUFFER_FLUSHING, events["a" /* default */].LEVEL_PTS_UPDATED, events["a" /* default */].LEVEL_UPDATED));
  10006. _this._msDuration = null;
  10007. // the value that we want to set mediaSource.duration to
  10008. _this._levelDuration = null;
  10009. // current stream state: true - for live broadcast, false - for VoD content
  10010. _this._live = null;
  10011. // cache the self generated object url to detect hijack of video tag
  10012. _this._objectUrl = null;
  10013. // Source Buffer listeners
  10014. _this.onsbue = _this.onSBUpdateEnd.bind(_this);
  10015. _this.onsbe = _this.onSBUpdateError.bind(_this);
  10016. _this.pendingTracks = {};
  10017. _this.tracks = {};
  10018. return _this;
  10019. }
  10020. BufferController.prototype.destroy = function destroy() {
  10021. event_handler.prototype.destroy.call(this);
  10022. };
  10023. BufferController.prototype.onLevelPtsUpdated = function onLevelPtsUpdated(data) {
  10024. var type = data.type;
  10025. var audioTrack = this.tracks.audio;
  10026. // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended)
  10027. // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset`
  10028. // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). At the time of change we issue
  10029. // `SourceBuffer.abort()` and adjusting `SourceBuffer.timestampOffset` if `SourceBuffer.updating` is false or awaiting `updateend`
  10030. // event if SB is in updating state.
  10031. // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486
  10032. if (type === 'audio' && audioTrack && audioTrack.container === 'audio/mpeg') {
  10033. // Chrome audio mp3 track
  10034. var audioBuffer = this.sourceBuffer.audio;
  10035. var delta = Math.abs(audioBuffer.timestampOffset - data.start);
  10036. // adjust timestamp offset if time delta is greater than 100ms
  10037. if (delta > 0.1) {
  10038. var updating = audioBuffer.updating;
  10039. try {
  10040. audioBuffer.abort();
  10041. } catch (err) {
  10042. updating = true;
  10043. logger["b" /* logger */].warn('can not abort audio buffer: ' + err);
  10044. }
  10045. if (!updating) {
  10046. logger["b" /* logger */].warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + data.start);
  10047. audioBuffer.timestampOffset = data.start;
  10048. } else {
  10049. this.audioTimestampOffset = data.start;
  10050. }
  10051. }
  10052. }
  10053. };
  10054. BufferController.prototype.onManifestParsed = function onManifestParsed(data) {
  10055. var audioExpected = data.audio,
  10056. videoExpected = data.video || data.levels.length && data.audio,
  10057. sourceBufferNb = 0;
  10058. // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller
  10059. // sourcebuffers will be created all at once when the expected nb of tracks will be reached
  10060. // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller
  10061. // it will contain the expected nb of source buffers, no need to compute it
  10062. if (data.altAudio && (audioExpected || videoExpected)) {
  10063. sourceBufferNb = (audioExpected ? 1 : 0) + (videoExpected ? 1 : 0);
  10064. logger["b" /* logger */].log(sourceBufferNb + ' sourceBuffer(s) expected');
  10065. }
  10066. this.sourceBufferNb = sourceBufferNb;
  10067. };
  10068. BufferController.prototype.onMediaAttaching = function onMediaAttaching(data) {
  10069. var media = this.media = data.media;
  10070. if (media) {
  10071. // setup the media source
  10072. var ms = this.mediaSource = new buffer_controller_MediaSource();
  10073. //Media Source listeners
  10074. this.onmso = this.onMediaSourceOpen.bind(this);
  10075. this.onmse = this.onMediaSourceEnded.bind(this);
  10076. this.onmsc = this.onMediaSourceClose.bind(this);
  10077. ms.addEventListener('sourceopen', this.onmso);
  10078. ms.addEventListener('sourceended', this.onmse);
  10079. ms.addEventListener('sourceclose', this.onmsc);
  10080. // link video and media Source
  10081. media.src = URL.createObjectURL(ms);
  10082. // cache the locally generated object url
  10083. this._objectUrl = media.src;
  10084. }
  10085. };
  10086. BufferController.prototype.onMediaDetaching = function onMediaDetaching() {
  10087. logger["b" /* logger */].log('media source detaching');
  10088. var ms = this.mediaSource;
  10089. if (ms) {
  10090. if (ms.readyState === 'open') {
  10091. try {
  10092. // endOfStream could trigger exception if any sourcebuffer is in updating state
  10093. // we don't really care about checking sourcebuffer state here,
  10094. // as we are anyway detaching the MediaSource
  10095. // let's just avoid this exception to propagate
  10096. ms.endOfStream();
  10097. } catch (err) {
  10098. logger["b" /* logger */].warn('onMediaDetaching:' + err.message + ' while calling endOfStream');
  10099. }
  10100. }
  10101. ms.removeEventListener('sourceopen', this.onmso);
  10102. ms.removeEventListener('sourceended', this.onmse);
  10103. ms.removeEventListener('sourceclose', this.onmsc);
  10104. // Detach properly the MediaSource from the HTMLMediaElement as
  10105. // suggested in https://github.com/w3c/media-source/issues/53.
  10106. if (this.media) {
  10107. URL.revokeObjectURL(this._objectUrl);
  10108. // clean up video tag src only if it's our own url. some external libraries might
  10109. // hijack the video tag and change its 'src' without destroying the Hls instance first
  10110. if (this.media.src === this._objectUrl) {
  10111. this.media.removeAttribute('src');
  10112. this.media.load();
  10113. } else {
  10114. logger["b" /* logger */].warn('media.src was changed by a third party - skip cleanup');
  10115. }
  10116. }
  10117. this.mediaSource = null;
  10118. this.media = null;
  10119. this._objectUrl = null;
  10120. this.pendingTracks = {};
  10121. this.tracks = {};
  10122. this.sourceBuffer = {};
  10123. this.flushRange = [];
  10124. this.segments = [];
  10125. this.appended = 0;
  10126. }
  10127. this.onmso = this.onmse = this.onmsc = null;
  10128. this.hls.trigger(events["a" /* default */].MEDIA_DETACHED);
  10129. };
  10130. BufferController.prototype.onMediaSourceOpen = function onMediaSourceOpen() {
  10131. logger["b" /* logger */].log('media source opened');
  10132. this.hls.trigger(events["a" /* default */].MEDIA_ATTACHED, {media: this.media});
  10133. var mediaSource = this.mediaSource;
  10134. if (mediaSource) {
  10135. // once received, don't listen anymore to sourceopen event
  10136. mediaSource.removeEventListener('sourceopen', this.onmso);
  10137. }
  10138. this.checkPendingTracks();
  10139. };
  10140. BufferController.prototype.checkPendingTracks = function checkPendingTracks() {
  10141. // if any buffer codecs pending, check if we have enough to create sourceBuffers
  10142. var pendingTracks = this.pendingTracks,
  10143. pendingTracksNb = Object.keys(pendingTracks).length;
  10144. // if any pending tracks and (if nb of pending tracks gt or equal than expected nb or if unknown expected nb)
  10145. if (pendingTracksNb && (this.sourceBufferNb <= pendingTracksNb || this.sourceBufferNb === 0)) {
  10146. // ok, let's create them now !
  10147. this.createSourceBuffers(pendingTracks);
  10148. this.pendingTracks = {};
  10149. // append any pending segments now !
  10150. this.doAppending();
  10151. }
  10152. };
  10153. BufferController.prototype.onMediaSourceClose = function onMediaSourceClose() {
  10154. logger["b" /* logger */].log('media source closed');
  10155. };
  10156. BufferController.prototype.onMediaSourceEnded = function onMediaSourceEnded() {
  10157. logger["b" /* logger */].log('media source ended');
  10158. };
  10159. BufferController.prototype.onSBUpdateEnd = function onSBUpdateEnd() {
  10160. // update timestampOffset
  10161. if (this.audioTimestampOffset) {
  10162. var audioBuffer = this.sourceBuffer.audio;
  10163. logger["b" /* logger */].warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + this.audioTimestampOffset);
  10164. audioBuffer.timestampOffset = this.audioTimestampOffset;
  10165. delete this.audioTimestampOffset;
  10166. }
  10167. if (this._needsFlush) {
  10168. this.doFlush();
  10169. }
  10170. if (this._needsEos) {
  10171. this.checkEos();
  10172. }
  10173. this.appending = false;
  10174. var parent = this.parent;
  10175. // count nb of pending segments waiting for appending on this sourcebuffer
  10176. var pending = this.segments.reduce(function (counter, segment) {
  10177. return segment.parent === parent ? counter + 1 : counter;
  10178. }, 0);
  10179. this.hls.trigger(events["a" /* default */].BUFFER_APPENDED, {parent: parent, pending: pending});
  10180. // don't append in flushing mode
  10181. if (!this._needsFlush) {
  10182. this.doAppending();
  10183. }
  10184. this.updateMediaElementDuration();
  10185. };
  10186. BufferController.prototype.onSBUpdateError = function onSBUpdateError(event) {
  10187. logger["b" /* logger */].error('sourceBuffer error:', event);
  10188. // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error
  10189. // this error might not always be fatal (it is fatal if decode error is set, in that case
  10190. // it will be followed by a mediaElement error ...)
  10191. this.hls.trigger(events["a" /* default */].ERROR, {
  10192. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  10193. details: errors["a" /* ErrorDetails */].BUFFER_APPENDING_ERROR,
  10194. fatal: false
  10195. });
  10196. // we don't need to do more than that, as accordin to the spec, updateend will be fired just after
  10197. };
  10198. BufferController.prototype.onBufferReset = function onBufferReset() {
  10199. var sourceBuffer = this.sourceBuffer;
  10200. for (var type in sourceBuffer) {
  10201. var sb = sourceBuffer[type];
  10202. try {
  10203. this.mediaSource.removeSourceBuffer(sb);
  10204. sb.removeEventListener('updateend', this.onsbue);
  10205. sb.removeEventListener('error', this.onsbe);
  10206. } catch (err) {
  10207. }
  10208. }
  10209. this.sourceBuffer = {};
  10210. this.flushRange = [];
  10211. this.segments = [];
  10212. this.appended = 0;
  10213. };
  10214. BufferController.prototype.onBufferCodecs = function onBufferCodecs(tracks) {
  10215. // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks
  10216. // if sourcebuffers already created, do nothing ...
  10217. if (Object.keys(this.sourceBuffer).length === 0) {
  10218. for (var trackName in tracks) {
  10219. this.pendingTracks[trackName] = tracks[trackName];
  10220. }
  10221. var mediaSource = this.mediaSource;
  10222. if (mediaSource && mediaSource.readyState === 'open') {
  10223. // try to create sourcebuffers if mediasource opened
  10224. this.checkPendingTracks();
  10225. }
  10226. }
  10227. };
  10228. BufferController.prototype.createSourceBuffers = function createSourceBuffers(tracks) {
  10229. var sourceBuffer = this.sourceBuffer,
  10230. mediaSource = this.mediaSource;
  10231. for (var trackName in tracks) {
  10232. if (!sourceBuffer[trackName]) {
  10233. var track = tracks[trackName];
  10234. // use levelCodec as first priority
  10235. var codec = track.levelCodec || track.codec;
  10236. var mimeType = track.container + ';codecs=' + codec;
  10237. logger["b" /* logger */].log('creating sourceBuffer(' + mimeType + ')');
  10238. try {
  10239. var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
  10240. sb.addEventListener('updateend', this.onsbue);
  10241. sb.addEventListener('error', this.onsbe);
  10242. this.tracks[trackName] = {codec: codec, container: track.container};
  10243. track.buffer = sb;
  10244. } catch (err) {
  10245. logger["b" /* logger */].error('error while trying to add sourceBuffer:' + err.message);
  10246. this.hls.trigger(events["a" /* default */].ERROR, {
  10247. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  10248. details: errors["a" /* ErrorDetails */].BUFFER_ADD_CODEC_ERROR,
  10249. fatal: false,
  10250. err: err,
  10251. mimeType: mimeType
  10252. });
  10253. }
  10254. }
  10255. }
  10256. this.hls.trigger(events["a" /* default */].BUFFER_CREATED, {tracks: tracks});
  10257. };
  10258. BufferController.prototype.onBufferAppending = function onBufferAppending(data) {
  10259. if (!this._needsFlush) {
  10260. if (!this.segments) {
  10261. this.segments = [data];
  10262. } else {
  10263. this.segments.push(data);
  10264. }
  10265. this.doAppending();
  10266. }
  10267. };
  10268. BufferController.prototype.onBufferAppendFail = function onBufferAppendFail(data) {
  10269. logger["b" /* logger */].error('sourceBuffer error:', data.event);
  10270. // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error
  10271. // this error might not always be fatal (it is fatal if decode error is set, in that case
  10272. // it will be followed by a mediaElement error ...)
  10273. this.hls.trigger(events["a" /* default */].ERROR, {
  10274. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  10275. details: errors["a" /* ErrorDetails */].BUFFER_APPENDING_ERROR,
  10276. fatal: false
  10277. });
  10278. };
  10279. // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos()
  10280. BufferController.prototype.onBufferEos = function onBufferEos(data) {
  10281. var sb = this.sourceBuffer;
  10282. var dataType = data.type;
  10283. for (var type in sb) {
  10284. if (!dataType || type === dataType) {
  10285. if (!sb[type].ended) {
  10286. sb[type].ended = true;
  10287. logger["b" /* logger */].log(type + ' sourceBuffer now EOS');
  10288. }
  10289. }
  10290. }
  10291. this.checkEos();
  10292. };
  10293. // if all source buffers are marked as ended, signal endOfStream() to MediaSource.
  10294. BufferController.prototype.checkEos = function checkEos() {
  10295. var sb = this.sourceBuffer,
  10296. mediaSource = this.mediaSource;
  10297. if (!mediaSource || mediaSource.readyState !== 'open') {
  10298. this._needsEos = false;
  10299. return;
  10300. }
  10301. for (var type in sb) {
  10302. var sbobj = sb[type];
  10303. if (!sbobj.ended) {
  10304. return;
  10305. }
  10306. if (sbobj.updating) {
  10307. this._needsEos = true;
  10308. return;
  10309. }
  10310. }
  10311. logger["b" /* logger */].log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
  10312. //Notify the media element that it now has all of the media data
  10313. try {
  10314. mediaSource.endOfStream();
  10315. } catch (e) {
  10316. logger["b" /* logger */].warn('exception while calling mediaSource.endOfStream()');
  10317. }
  10318. this._needsEos = false;
  10319. };
  10320. BufferController.prototype.onBufferFlushing = function onBufferFlushing(data) {
  10321. this.flushRange.push({start: data.startOffset, end: data.endOffset, type: data.type});
  10322. // attempt flush immediately
  10323. this.flushBufferCounter = 0;
  10324. this.doFlush();
  10325. };
  10326. BufferController.prototype.onLevelUpdated = function onLevelUpdated(_ref) {
  10327. var details = _ref.details;
  10328. if (details.fragments.length > 0) {
  10329. this._levelDuration = details.totalduration + details.fragments[0].start;
  10330. this._live = details.live;
  10331. this.updateMediaElementDuration();
  10332. }
  10333. };
  10334. /**
  10335. * Update Media Source duration to current level duration or override to Infinity if configuration parameter
  10336. * 'liveDurationInfinity` is set to `true`
  10337. * More details: https://github.com/video-dev/hls.js/issues/355
  10338. */
  10339. BufferController.prototype.updateMediaElementDuration = function updateMediaElementDuration() {
  10340. var config = this.hls.config;
  10341. var duration = void 0;
  10342. if (this._levelDuration === null || !this.media || !this.mediaSource || !this.sourceBuffer || this.media.readyState === 0 || this.mediaSource.readyState !== 'open') {
  10343. return;
  10344. }
  10345. for (var type in this.sourceBuffer) {
  10346. if (this.sourceBuffer[type].updating === true) {
  10347. // can't set duration whilst a buffer is updating
  10348. return;
  10349. }
  10350. }
  10351. duration = this.media.duration;
  10352. // initialise to the value that the media source is reporting
  10353. if (this._msDuration === null) {
  10354. this._msDuration = this.mediaSource.duration;
  10355. }
  10356. if (this._live === true && config.liveDurationInfinity === true) {
  10357. // Override duration to Infinity
  10358. logger["b" /* logger */].log('Media Source duration is set to Infinity');
  10359. this._msDuration = this.mediaSource.duration = Infinity;
  10360. } else if (this._levelDuration > this._msDuration && this._levelDuration > duration || duration === Infinity || isNaN(duration)) {
  10361. // levelDuration was the last value we set.
  10362. // not using mediaSource.duration as the browser may tweak this value
  10363. // only update Media Source duration if its value increase, this is to avoid
  10364. // flushing already buffered portion when switching between quality level
  10365. logger["b" /* logger */].log('Updating Media Source duration to ' + this._levelDuration.toFixed(3));
  10366. this._msDuration = this.mediaSource.duration = this._levelDuration;
  10367. }
  10368. };
  10369. BufferController.prototype.doFlush = function doFlush() {
  10370. // loop through all buffer ranges to flush
  10371. while (this.flushRange.length) {
  10372. var range = this.flushRange[0];
  10373. // flushBuffer will abort any buffer append in progress and flush Audio/Video Buffer
  10374. if (this.flushBuffer(range.start, range.end, range.type)) {
  10375. // range flushed, remove from flush array
  10376. this.flushRange.shift();
  10377. this.flushBufferCounter = 0;
  10378. } else {
  10379. this._needsFlush = true;
  10380. // avoid looping, wait for SB update end to retrigger a flush
  10381. return;
  10382. }
  10383. }
  10384. if (this.flushRange.length === 0) {
  10385. // everything flushed
  10386. this._needsFlush = false;
  10387. // let's recompute this.appended, which is used to avoid flush looping
  10388. var appended = 0;
  10389. var sourceBuffer = this.sourceBuffer;
  10390. try {
  10391. for (var type in sourceBuffer) {
  10392. appended += sourceBuffer[type].buffered.length;
  10393. }
  10394. } catch (error) {
  10395. // error could be thrown while accessing buffered, in case sourcebuffer has already been removed from MediaSource
  10396. // this is harmess at this stage, catch this to avoid reporting an internal exception
  10397. logger["b" /* logger */].error('error while accessing sourceBuffer.buffered');
  10398. }
  10399. this.appended = appended;
  10400. this.hls.trigger(events["a" /* default */].BUFFER_FLUSHED);
  10401. }
  10402. };
  10403. BufferController.prototype.doAppending = function doAppending() {
  10404. var hls = this.hls,
  10405. sourceBuffer = this.sourceBuffer,
  10406. segments = this.segments;
  10407. if (Object.keys(sourceBuffer).length) {
  10408. if (this.media.error) {
  10409. this.segments = [];
  10410. logger["b" /* logger */].error('trying to append although a media error occured, flush segment and abort');
  10411. return;
  10412. }
  10413. if (this.appending) {
  10414. //logger.log(`sb appending in progress`);
  10415. return;
  10416. }
  10417. if (segments && segments.length) {
  10418. var segment = segments.shift();
  10419. try {
  10420. var type = segment.type,
  10421. sb = sourceBuffer[type];
  10422. if (sb) {
  10423. if (!sb.updating) {
  10424. // reset sourceBuffer ended flag before appending segment
  10425. sb.ended = false;
  10426. //logger.log(`appending ${segment.content} ${type} SB, size:${segment.data.length}, ${segment.parent}`);
  10427. this.parent = segment.parent;
  10428. sb.appendBuffer(segment.data);
  10429. this.appendError = 0;
  10430. this.appended++;
  10431. this.appending = true;
  10432. } else {
  10433. segments.unshift(segment);
  10434. }
  10435. } else {
  10436. // in case we don't have any source buffer matching with this segment type,
  10437. // it means that Mediasource fails to create sourcebuffer
  10438. // discard this segment, and trigger update end
  10439. this.onSBUpdateEnd();
  10440. }
  10441. } catch (err) {
  10442. // in case any error occured while appending, put back segment in segments table
  10443. logger["b" /* logger */].error('error while trying to append buffer:' + err.message);
  10444. segments.unshift(segment);
  10445. var event = {
  10446. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  10447. parent: segment.parent
  10448. };
  10449. if (err.code !== 22) {
  10450. if (this.appendError) {
  10451. this.appendError++;
  10452. } else {
  10453. this.appendError = 1;
  10454. }
  10455. event.details = errors["a" /* ErrorDetails */].BUFFER_APPEND_ERROR;
  10456. /* with UHD content, we could get loop of quota exceeded error until
  10457. browser is able to evict some data from sourcebuffer. retrying help recovering this
  10458. */
  10459. if (this.appendError > hls.config.appendErrorMaxRetry) {
  10460. logger["b" /* logger */].log('fail ' + hls.config.appendErrorMaxRetry + ' times to append segment in sourceBuffer');
  10461. segments = [];
  10462. event.fatal = true;
  10463. hls.trigger(events["a" /* default */].ERROR, event);
  10464. return;
  10465. } else {
  10466. event.fatal = false;
  10467. hls.trigger(events["a" /* default */].ERROR, event);
  10468. }
  10469. } else {
  10470. // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror
  10471. // let's stop appending any segments, and report BUFFER_FULL_ERROR error
  10472. this.segments = [];
  10473. event.details = errors["a" /* ErrorDetails */].BUFFER_FULL_ERROR;
  10474. event.fatal = false;
  10475. hls.trigger(events["a" /* default */].ERROR, event);
  10476. return;
  10477. }
  10478. }
  10479. }
  10480. }
  10481. };
  10482. /*
  10483. flush specified buffered range,
  10484. return true once range has been flushed.
  10485. as sourceBuffer.remove() is asynchronous, flushBuffer will be retriggered on sourceBuffer update end
  10486. */
  10487. BufferController.prototype.flushBuffer = function flushBuffer(startOffset, endOffset, typeIn) {
  10488. var sb,
  10489. i,
  10490. bufStart,
  10491. bufEnd,
  10492. flushStart,
  10493. flushEnd,
  10494. sourceBuffer = this.sourceBuffer;
  10495. if (Object.keys(sourceBuffer).length) {
  10496. logger["b" /* logger */].log('flushBuffer,pos/start/end: ' + this.media.currentTime.toFixed(3) + '/' + startOffset + '/' + endOffset);
  10497. // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments
  10498. if (this.flushBufferCounter < this.appended) {
  10499. for (var type in sourceBuffer) {
  10500. // check if sourcebuffer type is defined (typeIn): if yes, let's only flush this one
  10501. // if no, let's flush all sourcebuffers
  10502. if (typeIn && type !== typeIn) {
  10503. continue;
  10504. }
  10505. sb = sourceBuffer[type];
  10506. // we are going to flush buffer, mark source buffer as 'not ended'
  10507. sb.ended = false;
  10508. if (!sb.updating) {
  10509. try {
  10510. for (i = 0; i < sb.buffered.length; i++) {
  10511. bufStart = sb.buffered.start(i);
  10512. bufEnd = sb.buffered.end(i);
  10513. // workaround firefox not able to properly flush multiple buffered range.
  10514. if (navigator.userAgent.toLowerCase().indexOf('firefox') !== -1 && endOffset === Number.POSITIVE_INFINITY) {
  10515. flushStart = startOffset;
  10516. flushEnd = endOffset;
  10517. } else {
  10518. flushStart = Math.max(bufStart, startOffset);
  10519. flushEnd = Math.min(bufEnd, endOffset);
  10520. }
  10521. /* sometimes sourcebuffer.remove() does not flush
  10522. the exact expected time range.
  10523. to avoid rounding issues/infinite loop,
  10524. only flush buffer range of length greater than 500ms.
  10525. */
  10526. if (Math.min(flushEnd, bufEnd) - flushStart > 0.5) {
  10527. this.flushBufferCounter++;
  10528. logger["b" /* logger */].log('flush ' + type + ' [' + flushStart + ',' + flushEnd + '], of [' + bufStart + ',' + bufEnd + '], pos:' + this.media.currentTime);
  10529. sb.remove(flushStart, flushEnd);
  10530. return false;
  10531. }
  10532. }
  10533. } catch (e) {
  10534. logger["b" /* logger */].warn('exception while accessing sourcebuffer, it might have been removed from MediaSource');
  10535. }
  10536. } else {
  10537. //logger.log('abort ' + type + ' append in progress');
  10538. // this will abort any appending in progress
  10539. //sb.abort();
  10540. logger["b" /* logger */].warn('cannot flush, sb updating in progress');
  10541. return false;
  10542. }
  10543. }
  10544. } else {
  10545. logger["b" /* logger */].warn('abort flushing too many retries');
  10546. }
  10547. logger["b" /* logger */].log('buffer flushed');
  10548. }
  10549. // everything flushed !
  10550. return true;
  10551. };
  10552. return BufferController;
  10553. }(event_handler);
  10554. /* harmony default export */
  10555. var buffer_controller = (buffer_controller_BufferController);
  10556. // CONCATENATED MODULE: ./src/controller/cap-level-controller.js
  10557. var cap_level_controller__createClass = function () {
  10558. function defineProperties(target, props) {
  10559. for (var i = 0; i < props.length; i++) {
  10560. var descriptor = props[i];
  10561. descriptor.enumerable = descriptor.enumerable || false;
  10562. descriptor.configurable = true;
  10563. if ("value" in descriptor) descriptor.writable = true;
  10564. Object.defineProperty(target, descriptor.key, descriptor);
  10565. }
  10566. }
  10567. return function (Constructor, protoProps, staticProps) {
  10568. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  10569. if (staticProps) defineProperties(Constructor, staticProps);
  10570. return Constructor;
  10571. };
  10572. }();
  10573. function cap_level_controller__classCallCheck(instance, Constructor) {
  10574. if (!(instance instanceof Constructor)) {
  10575. throw new TypeError("Cannot call a class as a function");
  10576. }
  10577. }
  10578. function cap_level_controller__possibleConstructorReturn(self, call) {
  10579. if (!self) {
  10580. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  10581. }
  10582. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  10583. }
  10584. function cap_level_controller__inherits(subClass, superClass) {
  10585. if (typeof superClass !== "function" && superClass !== null) {
  10586. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  10587. }
  10588. subClass.prototype = Object.create(superClass && superClass.prototype, {
  10589. constructor: {
  10590. value: subClass,
  10591. enumerable: false,
  10592. writable: true,
  10593. configurable: true
  10594. }
  10595. });
  10596. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  10597. }
  10598. /*
  10599. * cap stream level to media size dimension controller
  10600. */
  10601. var cap_level_controller_CapLevelController = function (_EventHandler) {
  10602. cap_level_controller__inherits(CapLevelController, _EventHandler);
  10603. function CapLevelController(hls) {
  10604. cap_level_controller__classCallCheck(this, CapLevelController);
  10605. return cap_level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FPS_DROP_LEVEL_CAPPING, events["a" /* default */].MEDIA_ATTACHING, events["a" /* default */].MANIFEST_PARSED));
  10606. }
  10607. CapLevelController.prototype.destroy = function destroy() {
  10608. if (this.hls.config.capLevelToPlayerSize) {
  10609. this.media = this.restrictedLevels = null;
  10610. this.autoLevelCapping = Number.POSITIVE_INFINITY;
  10611. if (this.timer) {
  10612. this.timer = clearInterval(this.timer);
  10613. }
  10614. }
  10615. };
  10616. CapLevelController.prototype.onFpsDropLevelCapping = function onFpsDropLevelCapping(data) {
  10617. // Don't add a restricted level more than once
  10618. if (CapLevelController.isLevelAllowed(data.droppedLevel, this.restrictedLevels)) {
  10619. this.restrictedLevels.push(data.droppedLevel);
  10620. }
  10621. };
  10622. CapLevelController.prototype.onMediaAttaching = function onMediaAttaching(data) {
  10623. this.media = data.media instanceof HTMLVideoElement ? data.media : null;
  10624. };
  10625. CapLevelController.prototype.onManifestParsed = function onManifestParsed(data) {
  10626. var hls = this.hls;
  10627. this.restrictedLevels = [];
  10628. if (hls.config.capLevelToPlayerSize) {
  10629. this.autoLevelCapping = Number.POSITIVE_INFINITY;
  10630. this.levels = data.levels;
  10631. hls.firstLevel = this.getMaxLevel(data.firstLevel);
  10632. clearInterval(this.timer);
  10633. this.timer = setInterval(this.detectPlayerSize.bind(this), 1000);
  10634. this.detectPlayerSize();
  10635. }
  10636. };
  10637. CapLevelController.prototype.detectPlayerSize = function detectPlayerSize() {
  10638. if (this.media) {
  10639. var levelsLength = this.levels ? this.levels.length : 0;
  10640. if (levelsLength) {
  10641. var hls = this.hls;
  10642. hls.autoLevelCapping = this.getMaxLevel(levelsLength - 1);
  10643. if (hls.autoLevelCapping > this.autoLevelCapping) {
  10644. // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
  10645. // usually happen when the user go to the fullscreen mode.
  10646. hls.streamController.nextLevelSwitch();
  10647. }
  10648. this.autoLevelCapping = hls.autoLevelCapping;
  10649. }
  10650. }
  10651. };
  10652. /*
  10653. * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled)
  10654. */
  10655. CapLevelController.prototype.getMaxLevel = function getMaxLevel(capLevelIndex) {
  10656. var _this2 = this;
  10657. if (!this.levels) {
  10658. return -1;
  10659. }
  10660. var validLevels = this.levels.filter(function (level, index) {
  10661. return CapLevelController.isLevelAllowed(index, _this2.restrictedLevels) && index <= capLevelIndex;
  10662. });
  10663. return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);
  10664. };
  10665. CapLevelController.isLevelAllowed = function isLevelAllowed(level) {
  10666. var restrictedLevels = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  10667. return restrictedLevels.indexOf(level) === -1;
  10668. };
  10669. CapLevelController.getMaxLevelByMediaSize = function getMaxLevelByMediaSize(levels, width, height) {
  10670. if (!levels || levels && !levels.length) {
  10671. return -1;
  10672. }
  10673. // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next
  10674. // to determine whether we've chosen the greatest bandwidth for the media's dimensions
  10675. var atGreatestBandiwdth = function atGreatestBandiwdth(curLevel, nextLevel) {
  10676. if (!nextLevel) {
  10677. return true;
  10678. }
  10679. return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height;
  10680. };
  10681. // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to
  10682. // the max level
  10683. var maxLevelIndex = levels.length - 1;
  10684. for (var i = 0; i < levels.length; i += 1) {
  10685. var level = levels[i];
  10686. if ((level.width >= width || level.height >= height) && atGreatestBandiwdth(level, levels[i + 1])) {
  10687. maxLevelIndex = i;
  10688. break;
  10689. }
  10690. }
  10691. return maxLevelIndex;
  10692. };
  10693. cap_level_controller__createClass(CapLevelController, [{
  10694. key: 'mediaWidth',
  10695. get: function get() {
  10696. var width = void 0;
  10697. var media = this.media;
  10698. if (media) {
  10699. width = media.width || media.clientWidth || media.offsetWidth;
  10700. width *= CapLevelController.contentScaleFactor;
  10701. }
  10702. return width;
  10703. }
  10704. }, {
  10705. key: 'mediaHeight',
  10706. get: function get() {
  10707. var height = void 0;
  10708. var media = this.media;
  10709. if (media) {
  10710. height = media.height || media.clientHeight || media.offsetHeight;
  10711. height *= CapLevelController.contentScaleFactor;
  10712. }
  10713. return height;
  10714. }
  10715. }], [{
  10716. key: 'contentScaleFactor',
  10717. get: function get() {
  10718. var pixelRatio = 1;
  10719. try {
  10720. pixelRatio = window.devicePixelRatio;
  10721. } catch (e) {
  10722. }
  10723. return pixelRatio;
  10724. }
  10725. }]);
  10726. return CapLevelController;
  10727. }(event_handler);
  10728. /* harmony default export */
  10729. var cap_level_controller = (cap_level_controller_CapLevelController);
  10730. // CONCATENATED MODULE: ./src/controller/fps-controller.js
  10731. function fps_controller__classCallCheck(instance, Constructor) {
  10732. if (!(instance instanceof Constructor)) {
  10733. throw new TypeError("Cannot call a class as a function");
  10734. }
  10735. }
  10736. function fps_controller__possibleConstructorReturn(self, call) {
  10737. if (!self) {
  10738. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  10739. }
  10740. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  10741. }
  10742. function fps_controller__inherits(subClass, superClass) {
  10743. if (typeof superClass !== "function" && superClass !== null) {
  10744. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  10745. }
  10746. subClass.prototype = Object.create(superClass && superClass.prototype, {
  10747. constructor: {
  10748. value: subClass,
  10749. enumerable: false,
  10750. writable: true,
  10751. configurable: true
  10752. }
  10753. });
  10754. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  10755. }
  10756. /*
  10757. * FPS Controller
  10758. */
  10759. var fps_controller_FPSController = function (_EventHandler) {
  10760. fps_controller__inherits(FPSController, _EventHandler);
  10761. function FPSController(hls) {
  10762. fps_controller__classCallCheck(this, FPSController);
  10763. return fps_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHING));
  10764. }
  10765. FPSController.prototype.destroy = function destroy() {
  10766. if (this.timer) {
  10767. clearInterval(this.timer);
  10768. }
  10769. this.isVideoPlaybackQualityAvailable = false;
  10770. };
  10771. FPSController.prototype.onMediaAttaching = function onMediaAttaching(data) {
  10772. var config = this.hls.config;
  10773. if (config.capLevelOnFPSDrop) {
  10774. var video = this.video = data.media instanceof HTMLVideoElement ? data.media : null;
  10775. if (typeof video.getVideoPlaybackQuality === 'function') {
  10776. this.isVideoPlaybackQualityAvailable = true;
  10777. }
  10778. clearInterval(this.timer);
  10779. this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod);
  10780. }
  10781. };
  10782. FPSController.prototype.checkFPS = function checkFPS(video, decodedFrames, droppedFrames) {
  10783. var currentTime = performance.now();
  10784. if (decodedFrames) {
  10785. if (this.lastTime) {
  10786. var currentPeriod = currentTime - this.lastTime,
  10787. currentDropped = droppedFrames - this.lastDroppedFrames,
  10788. currentDecoded = decodedFrames - this.lastDecodedFrames,
  10789. droppedFPS = 1000 * currentDropped / currentPeriod,
  10790. hls = this.hls;
  10791. hls.trigger(events["a" /* default */].FPS_DROP, {
  10792. currentDropped: currentDropped,
  10793. currentDecoded: currentDecoded,
  10794. totalDroppedFrames: droppedFrames
  10795. });
  10796. if (droppedFPS > 0) {
  10797. //logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
  10798. if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
  10799. var currentLevel = hls.currentLevel;
  10800. logger["b" /* logger */].warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
  10801. if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
  10802. currentLevel = currentLevel - 1;
  10803. hls.trigger(events["a" /* default */].FPS_DROP_LEVEL_CAPPING, {
  10804. level: currentLevel,
  10805. droppedLevel: hls.currentLevel
  10806. });
  10807. hls.autoLevelCapping = currentLevel;
  10808. hls.streamController.nextLevelSwitch();
  10809. }
  10810. }
  10811. }
  10812. }
  10813. this.lastTime = currentTime;
  10814. this.lastDroppedFrames = droppedFrames;
  10815. this.lastDecodedFrames = decodedFrames;
  10816. }
  10817. };
  10818. FPSController.prototype.checkFPSInterval = function checkFPSInterval() {
  10819. var video = this.video;
  10820. if (video) {
  10821. if (this.isVideoPlaybackQualityAvailable) {
  10822. var videoPlaybackQuality = video.getVideoPlaybackQuality();
  10823. this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames);
  10824. } else {
  10825. this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount);
  10826. }
  10827. }
  10828. };
  10829. return FPSController;
  10830. }(event_handler);
  10831. /* harmony default export */
  10832. var fps_controller = (fps_controller_FPSController);
  10833. // CONCATENATED MODULE: ./src/utils/xhr-loader.js
  10834. function xhr_loader__classCallCheck(instance, Constructor) {
  10835. if (!(instance instanceof Constructor)) {
  10836. throw new TypeError("Cannot call a class as a function");
  10837. }
  10838. }
  10839. /**
  10840. * XHR based logger
  10841. */
  10842. var xhr_loader_XhrLoader = function () {
  10843. function XhrLoader(config) {
  10844. xhr_loader__classCallCheck(this, XhrLoader);
  10845. if (config && config.xhrSetup) {
  10846. this.xhrSetup = config.xhrSetup;
  10847. }
  10848. }
  10849. XhrLoader.prototype.destroy = function destroy() {
  10850. this.abort();
  10851. this.loader = null;
  10852. };
  10853. XhrLoader.prototype.abort = function abort() {
  10854. var loader = this.loader;
  10855. if (loader && loader.readyState !== 4) {
  10856. this.stats.aborted = true;
  10857. loader.abort();
  10858. }
  10859. window.clearTimeout(this.requestTimeout);
  10860. this.requestTimeout = null;
  10861. window.clearTimeout(this.retryTimeout);
  10862. this.retryTimeout = null;
  10863. };
  10864. XhrLoader.prototype.load = function load(context, config, callbacks) {
  10865. this.context = context;
  10866. this.config = config;
  10867. this.callbacks = callbacks;
  10868. this.stats = {trequest: performance.now(), retry: 0};
  10869. this.retryDelay = config.retryDelay;
  10870. this.loadInternal();
  10871. };
  10872. XhrLoader.prototype.loadInternal = function loadInternal() {
  10873. var xhr,
  10874. context = this.context;
  10875. xhr = this.loader = new XMLHttpRequest();
  10876. var stats = this.stats;
  10877. stats.tfirst = 0;
  10878. stats.loaded = 0;
  10879. var xhrSetup = this.xhrSetup;
  10880. try {
  10881. if (xhrSetup) {
  10882. try {
  10883. xhrSetup(xhr, context.url);
  10884. } catch (e) {
  10885. // fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");}
  10886. // not working, as xhr.setRequestHeader expects xhr.readyState === OPEN
  10887. xhr.open('GET', context.url, true);
  10888. xhrSetup(xhr, context.url);
  10889. }
  10890. }
  10891. if (!xhr.readyState) {
  10892. xhr.open('GET', context.url, true);
  10893. }
  10894. } catch (e) {
  10895. // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
  10896. this.callbacks.onError({code: xhr.status, text: e.message}, context, xhr);
  10897. return;
  10898. }
  10899. if (context.rangeEnd) {
  10900. xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1));
  10901. }
  10902. xhr.onreadystatechange = this.readystatechange.bind(this);
  10903. xhr.onprogress = this.loadprogress.bind(this);
  10904. xhr.responseType = context.responseType;
  10905. // setup timeout before we perform request
  10906. this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout);
  10907. xhr.send();
  10908. };
  10909. XhrLoader.prototype.readystatechange = function readystatechange(event) {
  10910. var xhr = event.currentTarget,
  10911. readyState = xhr.readyState,
  10912. stats = this.stats,
  10913. context = this.context,
  10914. config = this.config;
  10915. // don't proceed if xhr has been aborted
  10916. if (stats.aborted) {
  10917. return;
  10918. }
  10919. // >= HEADERS_RECEIVED
  10920. if (readyState >= 2) {
  10921. // clear xhr timeout and rearm it if readyState less than 4
  10922. window.clearTimeout(this.requestTimeout);
  10923. if (stats.tfirst === 0) {
  10924. stats.tfirst = Math.max(performance.now(), stats.trequest);
  10925. }
  10926. if (readyState === 4) {
  10927. var status = xhr.status;
  10928. // http status between 200 to 299 are all successful
  10929. if (status >= 200 && status < 300) {
  10930. stats.tload = Math.max(stats.tfirst, performance.now());
  10931. var data = void 0,
  10932. len = void 0;
  10933. if (context.responseType === 'arraybuffer') {
  10934. data = xhr.response;
  10935. len = data.byteLength;
  10936. } else {
  10937. data = xhr.responseText;
  10938. len = data.length;
  10939. }
  10940. stats.loaded = stats.total = len;
  10941. var response = {url: xhr.responseURL, data: data};
  10942. this.callbacks.onSuccess(response, stats, context, xhr);
  10943. } else {
  10944. // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
  10945. if (stats.retry >= config.maxRetry || status >= 400 && status < 499) {
  10946. logger["b" /* logger */].error(status + ' while loading ' + context.url);
  10947. this.callbacks.onError({code: status, text: xhr.statusText}, context, xhr);
  10948. } else {
  10949. // retry
  10950. logger["b" /* logger */].warn(status + ' while loading ' + context.url + ', retrying in ' + this.retryDelay + '...');
  10951. // aborts and resets internal state
  10952. this.destroy();
  10953. // schedule retry
  10954. this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay);
  10955. // set exponential backoff
  10956. this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay);
  10957. stats.retry++;
  10958. }
  10959. }
  10960. } else {
  10961. // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet
  10962. this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout);
  10963. }
  10964. }
  10965. };
  10966. XhrLoader.prototype.loadtimeout = function loadtimeout() {
  10967. logger["b" /* logger */].warn('timeout while loading ' + this.context.url);
  10968. this.callbacks.onTimeout(this.stats, this.context, null);
  10969. };
  10970. XhrLoader.prototype.loadprogress = function loadprogress(event) {
  10971. var xhr = event.currentTarget,
  10972. stats = this.stats;
  10973. stats.loaded = event.loaded;
  10974. if (event.lengthComputable) {
  10975. stats.total = event.total;
  10976. }
  10977. var onProgress = this.callbacks.onProgress;
  10978. if (onProgress) {
  10979. // third arg is to provide on progress data
  10980. onProgress(stats, this.context, null, xhr);
  10981. }
  10982. };
  10983. return XhrLoader;
  10984. }();
  10985. /* harmony default export */
  10986. var xhr_loader = (xhr_loader_XhrLoader);
  10987. // CONCATENATED MODULE: ./src/controller/audio-track-controller.js
  10988. var audio_track_controller__createClass = function () {
  10989. function defineProperties(target, props) {
  10990. for (var i = 0; i < props.length; i++) {
  10991. var descriptor = props[i];
  10992. descriptor.enumerable = descriptor.enumerable || false;
  10993. descriptor.configurable = true;
  10994. if ("value" in descriptor) descriptor.writable = true;
  10995. Object.defineProperty(target, descriptor.key, descriptor);
  10996. }
  10997. }
  10998. return function (Constructor, protoProps, staticProps) {
  10999. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  11000. if (staticProps) defineProperties(Constructor, staticProps);
  11001. return Constructor;
  11002. };
  11003. }();
  11004. function audio_track_controller__classCallCheck(instance, Constructor) {
  11005. if (!(instance instanceof Constructor)) {
  11006. throw new TypeError("Cannot call a class as a function");
  11007. }
  11008. }
  11009. function audio_track_controller__possibleConstructorReturn(self, call) {
  11010. if (!self) {
  11011. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  11012. }
  11013. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  11014. }
  11015. function audio_track_controller__inherits(subClass, superClass) {
  11016. if (typeof superClass !== "function" && superClass !== null) {
  11017. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  11018. }
  11019. subClass.prototype = Object.create(superClass && superClass.prototype, {
  11020. constructor: {
  11021. value: subClass,
  11022. enumerable: false,
  11023. writable: true,
  11024. configurable: true
  11025. }
  11026. });
  11027. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  11028. }
  11029. /*
  11030. * audio track controller
  11031. */
  11032. var audio_track_controller_AudioTrackController = function (_EventHandler) {
  11033. audio_track_controller__inherits(AudioTrackController, _EventHandler);
  11034. function AudioTrackController(hls) {
  11035. audio_track_controller__classCallCheck(this, AudioTrackController);
  11036. var _this = audio_track_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MANIFEST_LOADING, events["a" /* default */].MANIFEST_PARSED, events["a" /* default */].AUDIO_TRACK_LOADED, events["a" /* default */].ERROR));
  11037. _this.ticks = 0;
  11038. _this.ontick = _this.tick.bind(_this);
  11039. return _this;
  11040. }
  11041. AudioTrackController.prototype.destroy = function destroy() {
  11042. this.cleanTimer();
  11043. event_handler.prototype.destroy.call(this);
  11044. };
  11045. AudioTrackController.prototype.cleanTimer = function cleanTimer() {
  11046. if (this.timer) {
  11047. clearTimeout(this.timer);
  11048. this.timer = null;
  11049. }
  11050. };
  11051. AudioTrackController.prototype.tick = function tick() {
  11052. this.ticks++;
  11053. if (this.ticks === 1) {
  11054. this.doTick();
  11055. if (this.ticks > 1) {
  11056. setTimeout(this.tick, 1);
  11057. }
  11058. this.ticks = 0;
  11059. }
  11060. };
  11061. AudioTrackController.prototype.doTick = function doTick() {
  11062. this.updateTrack(this.trackId);
  11063. };
  11064. AudioTrackController.prototype.onError = function onError(data) {
  11065. if (data.fatal && data.type === errors["b" /* ErrorTypes */].NETWORK_ERROR) {
  11066. this.cleanTimer();
  11067. }
  11068. };
  11069. AudioTrackController.prototype.onManifestLoading = function onManifestLoading() {
  11070. // reset audio tracks on manifest loading
  11071. this.tracks = [];
  11072. this.trackId = -1;
  11073. };
  11074. AudioTrackController.prototype.onManifestParsed = function onManifestParsed(data) {
  11075. var _this2 = this;
  11076. var tracks = data.audioTracks || [];
  11077. var defaultFound = false;
  11078. this.tracks = tracks;
  11079. this.hls.trigger(events["a" /* default */].AUDIO_TRACKS_UPDATED, {audioTracks: tracks});
  11080. // loop through available audio tracks and autoselect default if needed
  11081. var id = 0;
  11082. tracks.forEach(function (track) {
  11083. if (track.default && !defaultFound) {
  11084. _this2.audioTrack = id;
  11085. defaultFound = true;
  11086. return;
  11087. }
  11088. id++;
  11089. });
  11090. if (defaultFound === false && tracks.length) {
  11091. logger["b" /* logger */].log('no default audio track defined, use first audio track as default');
  11092. this.audioTrack = 0;
  11093. }
  11094. };
  11095. AudioTrackController.prototype.onAudioTrackLoaded = function onAudioTrackLoaded(data) {
  11096. if (data.id < this.tracks.length) {
  11097. logger["b" /* logger */].log('audioTrack ' + data.id + ' loaded');
  11098. this.tracks[data.id].details = data.details;
  11099. // check if current playlist is a live playlist
  11100. if (data.details.live && !this.timer) {
  11101. // if live playlist we will have to reload it periodically
  11102. // set reload period to playlist target duration
  11103. this.timer = setInterval(this.ontick, 1000 * data.details.targetduration);
  11104. }
  11105. if (!data.details.live && this.timer) {
  11106. // playlist is not live and timer is armed : stopping it
  11107. this.cleanTimer();
  11108. }
  11109. }
  11110. };
  11111. /** get alternate audio tracks list from playlist **/
  11112. AudioTrackController.prototype.setAudioTrackInternal = function setAudioTrackInternal(newId) {
  11113. // check if level idx is valid
  11114. if (newId >= 0 && newId < this.tracks.length) {
  11115. // stopping live reloading timer if any
  11116. this.cleanTimer();
  11117. this.trackId = newId;
  11118. logger["b" /* logger */].log('switching to audioTrack ' + newId);
  11119. var audioTrack = this.tracks[newId],
  11120. hls = this.hls,
  11121. type = audioTrack.type,
  11122. url = audioTrack.url,
  11123. eventObj = {id: newId, type: type, url: url};
  11124. // keep AUDIO_TRACK_SWITCH for legacy reason
  11125. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCH, eventObj);
  11126. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHING, eventObj);
  11127. // check if we need to load playlist for this audio Track
  11128. var details = audioTrack.details;
  11129. if (url && (details === undefined || details.live === true)) {
  11130. // track not retrieved yet, or live playlist we need to (re)load it
  11131. logger["b" /* logger */].log('(re)loading playlist for audioTrack ' + newId);
  11132. hls.trigger(events["a" /* default */].AUDIO_TRACK_LOADING, {url: url, id: newId});
  11133. }
  11134. }
  11135. };
  11136. AudioTrackController.prototype.updateTrack = function updateTrack(newId) {
  11137. // check if level idx is valid
  11138. if (newId >= 0 && newId < this.tracks.length) {
  11139. // stopping live reloading timer if any
  11140. this.cleanTimer();
  11141. this.trackId = newId;
  11142. logger["b" /* logger */].log('updating audioTrack ' + newId);
  11143. var audioTrack = this.tracks[newId],
  11144. url = audioTrack.url;
  11145. // check if we need to load playlist for this audio Track
  11146. var details = audioTrack.details;
  11147. if (url && (details === undefined || details.live === true)) {
  11148. // track not retrieved yet, or live playlist we need to (re)load it
  11149. logger["b" /* logger */].log('(re)loading playlist for audioTrack ' + newId);
  11150. this.hls.trigger(events["a" /* default */].AUDIO_TRACK_LOADING, {url: url, id: newId});
  11151. }
  11152. }
  11153. };
  11154. audio_track_controller__createClass(AudioTrackController, [{
  11155. key: 'audioTracks',
  11156. get: function get() {
  11157. return this.tracks;
  11158. }
  11159. /** get index of the selected audio track (index in audio track lists) **/
  11160. }, {
  11161. key: 'audioTrack',
  11162. get: function get() {
  11163. return this.trackId;
  11164. }
  11165. /** select an audio track, based on its index in audio track lists**/
  11166. ,
  11167. set: function set(audioTrackId) {
  11168. if (this.trackId !== audioTrackId || this.tracks[audioTrackId].details === undefined) {
  11169. this.setAudioTrackInternal(audioTrackId);
  11170. }
  11171. }
  11172. }]);
  11173. return AudioTrackController;
  11174. }(event_handler);
  11175. /* harmony default export */
  11176. var audio_track_controller = (audio_track_controller_AudioTrackController);
  11177. // CONCATENATED MODULE: ./src/controller/audio-stream-controller.js
  11178. var audio_stream_controller__createClass = function () {
  11179. function defineProperties(target, props) {
  11180. for (var i = 0; i < props.length; i++) {
  11181. var descriptor = props[i];
  11182. descriptor.enumerable = descriptor.enumerable || false;
  11183. descriptor.configurable = true;
  11184. if ("value" in descriptor) descriptor.writable = true;
  11185. Object.defineProperty(target, descriptor.key, descriptor);
  11186. }
  11187. }
  11188. return function (Constructor, protoProps, staticProps) {
  11189. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  11190. if (staticProps) defineProperties(Constructor, staticProps);
  11191. return Constructor;
  11192. };
  11193. }();
  11194. function audio_stream_controller__classCallCheck(instance, Constructor) {
  11195. if (!(instance instanceof Constructor)) {
  11196. throw new TypeError("Cannot call a class as a function");
  11197. }
  11198. }
  11199. function audio_stream_controller__possibleConstructorReturn(self, call) {
  11200. if (!self) {
  11201. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  11202. }
  11203. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  11204. }
  11205. function audio_stream_controller__inherits(subClass, superClass) {
  11206. if (typeof superClass !== "function" && superClass !== null) {
  11207. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  11208. }
  11209. subClass.prototype = Object.create(superClass && superClass.prototype, {
  11210. constructor: {
  11211. value: subClass,
  11212. enumerable: false,
  11213. writable: true,
  11214. configurable: true
  11215. }
  11216. });
  11217. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  11218. }
  11219. /*
  11220. * Audio Stream Controller
  11221. */
  11222. var audio_stream_controller_State = {
  11223. STOPPED: 'STOPPED',
  11224. STARTING: 'STARTING',
  11225. IDLE: 'IDLE',
  11226. PAUSED: 'PAUSED',
  11227. KEY_LOADING: 'KEY_LOADING',
  11228. FRAG_LOADING: 'FRAG_LOADING',
  11229. FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',
  11230. WAITING_TRACK: 'WAITING_TRACK',
  11231. PARSING: 'PARSING',
  11232. PARSED: 'PARSED',
  11233. BUFFER_FLUSHING: 'BUFFER_FLUSHING',
  11234. ENDED: 'ENDED',
  11235. ERROR: 'ERROR',
  11236. WAITING_INIT_PTS: 'WAITING_INIT_PTS'
  11237. };
  11238. var audio_stream_controller_AudioStreamController = function (_TaskLoop) {
  11239. audio_stream_controller__inherits(AudioStreamController, _TaskLoop);
  11240. function AudioStreamController(hls) {
  11241. audio_stream_controller__classCallCheck(this, AudioStreamController);
  11242. var _this = audio_stream_controller__possibleConstructorReturn(this, _TaskLoop.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].AUDIO_TRACKS_UPDATED, events["a" /* default */].AUDIO_TRACK_SWITCHING, events["a" /* default */].AUDIO_TRACK_LOADED, events["a" /* default */].KEY_LOADED, events["a" /* default */].FRAG_LOADED, events["a" /* default */].FRAG_PARSING_INIT_SEGMENT, events["a" /* default */].FRAG_PARSING_DATA, events["a" /* default */].FRAG_PARSED, events["a" /* default */].ERROR, events["a" /* default */].BUFFER_RESET, events["a" /* default */].BUFFER_CREATED, events["a" /* default */].BUFFER_APPENDED, events["a" /* default */].BUFFER_FLUSHED, events["a" /* default */].INIT_PTS_FOUND));
  11243. _this.config = hls.config;
  11244. _this.audioCodecSwap = false;
  11245. _this._state = audio_stream_controller_State.STOPPED;
  11246. _this.initPTS = [];
  11247. _this.waitingFragment = null;
  11248. _this.videoTrackCC = null;
  11249. return _this;
  11250. }
  11251. AudioStreamController.prototype.onHandlerDestroying = function onHandlerDestroying() {
  11252. this.stopLoad();
  11253. };
  11254. AudioStreamController.prototype.onHandlerDestroyed = function onHandlerDestroyed() {
  11255. this.state = audio_stream_controller_State.STOPPED;
  11256. };
  11257. //Signal that video PTS was found
  11258. AudioStreamController.prototype.onInitPtsFound = function onInitPtsFound(data) {
  11259. var demuxerId = data.id,
  11260. cc = data.frag.cc,
  11261. initPTS = data.initPTS;
  11262. if (demuxerId === 'main') {
  11263. //Always update the new INIT PTS
  11264. //Can change due level switch
  11265. this.initPTS[cc] = initPTS;
  11266. this.videoTrackCC = cc;
  11267. logger["b" /* logger */].log('InitPTS for cc: ' + cc + ' found from video track: ' + initPTS);
  11268. //If we are waiting we need to demux/remux the waiting frag
  11269. //With the new initPTS
  11270. if (this.state === audio_stream_controller_State.WAITING_INIT_PTS) {
  11271. this.tick();
  11272. }
  11273. }
  11274. };
  11275. AudioStreamController.prototype.startLoad = function startLoad(startPosition) {
  11276. if (this.tracks) {
  11277. var lastCurrentTime = this.lastCurrentTime;
  11278. this.stopLoad();
  11279. this.setInterval(100);
  11280. this.fragLoadError = 0;
  11281. if (lastCurrentTime > 0 && startPosition === -1) {
  11282. logger["b" /* logger */].log('audio:override startPosition with lastCurrentTime @' + lastCurrentTime.toFixed(3));
  11283. this.state = audio_stream_controller_State.IDLE;
  11284. } else {
  11285. this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition;
  11286. this.state = audio_stream_controller_State.STARTING;
  11287. }
  11288. this.nextLoadPosition = this.startPosition = this.lastCurrentTime;
  11289. this.tick();
  11290. } else {
  11291. this.startPosition = startPosition;
  11292. this.state = audio_stream_controller_State.STOPPED;
  11293. }
  11294. };
  11295. AudioStreamController.prototype.stopLoad = function stopLoad() {
  11296. var frag = this.fragCurrent;
  11297. if (frag) {
  11298. if (frag.loader) {
  11299. frag.loader.abort();
  11300. }
  11301. this.fragCurrent = null;
  11302. }
  11303. this.fragPrevious = null;
  11304. if (this.demuxer) {
  11305. this.demuxer.destroy();
  11306. this.demuxer = null;
  11307. }
  11308. this.state = audio_stream_controller_State.STOPPED;
  11309. };
  11310. AudioStreamController.prototype.doTick = function doTick() {
  11311. var pos,
  11312. track,
  11313. trackDetails,
  11314. hls = this.hls,
  11315. config = hls.config;
  11316. //logger.log('audioStream:' + this.state);
  11317. switch (this.state) {
  11318. case audio_stream_controller_State.ERROR:
  11319. //don't do anything in error state to avoid breaking further ...
  11320. case audio_stream_controller_State.PAUSED:
  11321. //don't do anything in paused state either ...
  11322. case audio_stream_controller_State.BUFFER_FLUSHING:
  11323. break;
  11324. case audio_stream_controller_State.STARTING:
  11325. this.state = audio_stream_controller_State.WAITING_TRACK;
  11326. this.loadedmetadata = false;
  11327. break;
  11328. case audio_stream_controller_State.IDLE:
  11329. var tracks = this.tracks;
  11330. // audio tracks not received => exit loop
  11331. if (!tracks) {
  11332. break;
  11333. }
  11334. // if video not attached AND
  11335. // start fragment already requested OR start frag prefetch disable
  11336. // exit loop
  11337. // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
  11338. if (!this.media && (this.startFragRequested || !config.startFragPrefetch)) {
  11339. break;
  11340. }
  11341. // determine next candidate fragment to be loaded, based on current position and
  11342. // end of buffer position
  11343. // if we have not yet loaded any fragment, start loading from start position
  11344. if (this.loadedmetadata) {
  11345. pos = this.media.currentTime;
  11346. } else {
  11347. pos = this.nextLoadPosition;
  11348. if (pos === undefined) {
  11349. break;
  11350. }
  11351. }
  11352. var media = this.mediaBuffer ? this.mediaBuffer : this.media,
  11353. videoBuffer = this.videoBuffer ? this.videoBuffer : this.media,
  11354. bufferInfo = buffer_helper.bufferInfo(media, pos, config.maxBufferHole),
  11355. mainBufferInfo = buffer_helper.bufferInfo(videoBuffer, pos, config.maxBufferHole),
  11356. bufferLen = bufferInfo.len,
  11357. bufferEnd = bufferInfo.end,
  11358. fragPrevious = this.fragPrevious,
  11359. // ensure we buffer at least config.maxBufferLength (default 30s)
  11360. // once we reach that threshold, don't buffer more than video (mainBufferInfo.len)
  11361. maxBufLen = Math.max(config.maxBufferLength, mainBufferInfo.len),
  11362. audioSwitch = this.audioSwitch,
  11363. trackId = this.trackId;
  11364. // if buffer length is less than maxBufLen try to load a new fragment
  11365. if ((bufferLen < maxBufLen || audioSwitch) && trackId < tracks.length) {
  11366. trackDetails = tracks[trackId].details;
  11367. // if track info not retrieved yet, switch state and wait for track retrieval
  11368. if (typeof trackDetails === 'undefined') {
  11369. this.state = audio_stream_controller_State.WAITING_TRACK;
  11370. break;
  11371. }
  11372. // check if we need to finalize media stream
  11373. // we just got done loading the final fragment and there is no other buffered range after ...
  11374. // rationale is that in case there are any buffered ranges after, it means that there are unbuffered portion in between
  11375. // so we should not switch to ENDED in that case, to be able to buffer them
  11376. if (!audioSwitch && !trackDetails.live && fragPrevious && fragPrevious.sn === trackDetails.endSN && !bufferInfo.nextStart) {
  11377. // if we are not seeking or if we are seeking but everything (almost) til the end is buffered, let's signal eos
  11378. // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference when switching
  11379. // between different renditions. using half frag duration should help cope with these cases.
  11380. if (!this.media.seeking || this.media.duration - bufferEnd < fragPrevious.duration / 2) {
  11381. // Finalize the media stream
  11382. this.hls.trigger(events["a" /* default */].BUFFER_EOS, {type: 'audio'});
  11383. this.state = audio_stream_controller_State.ENDED;
  11384. break;
  11385. }
  11386. }
  11387. // find fragment index, contiguous with end of buffer position
  11388. var fragments = trackDetails.fragments,
  11389. fragLen = fragments.length,
  11390. start = fragments[0].start,
  11391. end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration,
  11392. frag = void 0;
  11393. // When switching audio track, reload audio as close as possible to currentTime
  11394. if (audioSwitch) {
  11395. if (trackDetails.live && !trackDetails.PTSKnown) {
  11396. logger["b" /* logger */].log('switching audiotrack, live stream, unknown PTS,load first fragment');
  11397. bufferEnd = 0;
  11398. } else {
  11399. bufferEnd = pos;
  11400. // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime
  11401. if (trackDetails.PTSKnown && pos < start) {
  11402. // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start
  11403. if (bufferInfo.end > start || bufferInfo.nextStart) {
  11404. logger["b" /* logger */].log('alt audio track ahead of main track, seek to start of alt audio track');
  11405. this.media.currentTime = start + 0.05;
  11406. } else {
  11407. return;
  11408. }
  11409. }
  11410. }
  11411. }
  11412. if (trackDetails.initSegment && !trackDetails.initSegment.data) {
  11413. frag = trackDetails.initSegment;
  11414. }
  11415. // if bufferEnd before start of playlist, load first fragment
  11416. else if (bufferEnd <= start) {
  11417. frag = fragments[0];
  11418. if (this.videoTrackCC !== null && frag.cc !== this.videoTrackCC) {
  11419. // Ensure we find a fragment which matches the continuity of the video track
  11420. frag = findFragWithCC(fragments, this.videoTrackCC);
  11421. }
  11422. if (trackDetails.live && frag.loadIdx && frag.loadIdx === this.fragLoadIdx) {
  11423. // we just loaded this first fragment, and we are still lagging behind the start of the live playlist
  11424. // let's force seek to start
  11425. var nextBuffered = bufferInfo.nextStart ? bufferInfo.nextStart : start;
  11426. logger["b" /* logger */].log('no alt audio available @currentTime:' + this.media.currentTime + ', seeking @' + (nextBuffered + 0.05));
  11427. this.media.currentTime = nextBuffered + 0.05;
  11428. return;
  11429. }
  11430. } else {
  11431. var foundFrag = void 0;
  11432. var maxFragLookUpTolerance = config.maxFragLookUpTolerance;
  11433. var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined;
  11434. var fragmentWithinToleranceTest = function fragmentWithinToleranceTest(candidate) {
  11435. // offset should be within fragment boundary - config.maxFragLookUpTolerance
  11436. // this is to cope with situations like
  11437. // bufferEnd = 9.991
  11438. // frag[Ø] : [0,10]
  11439. // frag[1] : [10,20]
  11440. // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
  11441. // frag start frag start+duration
  11442. // |-----------------------------|
  11443. // <---> <--->
  11444. // ...--------><-----------------------------><---------....
  11445. // previous frag matching fragment next frag
  11446. // return -1 return 0 return 1
  11447. //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
  11448. // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
  11449. var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration);
  11450. if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {
  11451. return 1;
  11452. } // if maxFragLookUpTolerance will have negative value then don't return -1 for first element
  11453. else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {
  11454. return -1;
  11455. }
  11456. return 0;
  11457. };
  11458. if (bufferEnd < end) {
  11459. if (bufferEnd > end - maxFragLookUpTolerance) {
  11460. maxFragLookUpTolerance = 0;
  11461. }
  11462. // Prefer the next fragment if it's within tolerance
  11463. if (fragNext && !fragmentWithinToleranceTest(fragNext)) {
  11464. foundFrag = fragNext;
  11465. } else {
  11466. foundFrag = binary_search.search(fragments, fragmentWithinToleranceTest);
  11467. }
  11468. } else {
  11469. // reach end of playlist
  11470. foundFrag = fragments[fragLen - 1];
  11471. }
  11472. if (foundFrag) {
  11473. frag = foundFrag;
  11474. start = foundFrag.start;
  11475. //logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn);
  11476. if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) {
  11477. if (frag.sn < trackDetails.endSN) {
  11478. frag = fragments[frag.sn + 1 - trackDetails.startSN];
  11479. logger["b" /* logger */].log('SN just loaded, load next one: ' + frag.sn);
  11480. } else {
  11481. frag = null;
  11482. }
  11483. }
  11484. }
  11485. }
  11486. if (frag) {
  11487. //logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
  11488. if (frag.decryptdata && frag.decryptdata.uri != null && frag.decryptdata.key == null) {
  11489. logger["b" /* logger */].log('Loading key for ' + frag.sn + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + trackId);
  11490. this.state = audio_stream_controller_State.KEY_LOADING;
  11491. hls.trigger(events["a" /* default */].KEY_LOADING, {frag: frag});
  11492. } else {
  11493. logger["b" /* logger */].log('Loading ' + frag.sn + ', cc: ' + frag.cc + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + trackId + ', currentTime:' + pos + ',bufferEnd:' + bufferEnd.toFixed(3));
  11494. // ensure that we are not reloading the same fragments in loop ...
  11495. if (this.fragLoadIdx !== undefined) {
  11496. this.fragLoadIdx++;
  11497. } else {
  11498. this.fragLoadIdx = 0;
  11499. }
  11500. if (frag.loadCounter) {
  11501. frag.loadCounter++;
  11502. var maxThreshold = config.fragLoadingLoopThreshold;
  11503. // if this frag has already been loaded 3 times, and if it has been reloaded recently
  11504. if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) {
  11505. hls.trigger(events["a" /* default */].ERROR, {
  11506. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  11507. details: errors["a" /* ErrorDetails */].FRAG_LOOP_LOADING_ERROR,
  11508. fatal: false,
  11509. frag: frag
  11510. });
  11511. return;
  11512. }
  11513. } else {
  11514. frag.loadCounter = 1;
  11515. }
  11516. frag.loadIdx = this.fragLoadIdx;
  11517. this.fragCurrent = frag;
  11518. this.startFragRequested = true;
  11519. if (!isNaN(frag.sn)) {
  11520. this.nextLoadPosition = frag.start + frag.duration;
  11521. }
  11522. hls.trigger(events["a" /* default */].FRAG_LOADING, {frag: frag});
  11523. this.state = audio_stream_controller_State.FRAG_LOADING;
  11524. }
  11525. }
  11526. }
  11527. break;
  11528. case audio_stream_controller_State.WAITING_TRACK:
  11529. track = this.tracks[this.trackId];
  11530. // check if playlist is already loaded
  11531. if (track && track.details) {
  11532. this.state = audio_stream_controller_State.IDLE;
  11533. }
  11534. break;
  11535. case audio_stream_controller_State.FRAG_LOADING_WAITING_RETRY:
  11536. var now = performance.now();
  11537. var retryDate = this.retryDate;
  11538. media = this.media;
  11539. var isSeeking = media && media.seeking;
  11540. // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
  11541. if (!retryDate || now >= retryDate || isSeeking) {
  11542. logger["b" /* logger */].log('audioStreamController: retryDate reached, switch back to IDLE state');
  11543. this.state = audio_stream_controller_State.IDLE;
  11544. }
  11545. break;
  11546. case audio_stream_controller_State.WAITING_INIT_PTS:
  11547. var videoTrackCC = this.videoTrackCC;
  11548. if (this.initPTS[videoTrackCC] === undefined) {
  11549. break;
  11550. }
  11551. // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS
  11552. var waitingFrag = this.waitingFragment;
  11553. if (waitingFrag) {
  11554. var waitingFragCC = waitingFrag.frag.cc;
  11555. if (videoTrackCC !== waitingFragCC) {
  11556. track = this.tracks[this.trackId];
  11557. if (track.details && track.details.live) {
  11558. logger["b" /* logger */].warn('Waiting fragment CC (' + waitingFragCC + ') does not match video track CC (' + videoTrackCC + ')');
  11559. this.waitingFragment = null;
  11560. this.state = audio_stream_controller_State.IDLE;
  11561. }
  11562. } else {
  11563. this.state = audio_stream_controller_State.FRAG_LOADING;
  11564. this.onFragLoaded(this.waitingFragment);
  11565. this.waitingFragment = null;
  11566. }
  11567. } else {
  11568. this.state = audio_stream_controller_State.IDLE;
  11569. }
  11570. break;
  11571. case audio_stream_controller_State.STOPPED:
  11572. case audio_stream_controller_State.FRAG_LOADING:
  11573. case audio_stream_controller_State.PARSING:
  11574. case audio_stream_controller_State.PARSED:
  11575. case audio_stream_controller_State.ENDED:
  11576. break;
  11577. default:
  11578. break;
  11579. }
  11580. };
  11581. AudioStreamController.prototype.onMediaAttached = function onMediaAttached(data) {
  11582. var media = this.media = this.mediaBuffer = data.media;
  11583. this.onvseeking = this.onMediaSeeking.bind(this);
  11584. this.onvended = this.onMediaEnded.bind(this);
  11585. media.addEventListener('seeking', this.onvseeking);
  11586. media.addEventListener('ended', this.onvended);
  11587. var config = this.config;
  11588. if (this.tracks && config.autoStartLoad) {
  11589. this.startLoad(config.startPosition);
  11590. }
  11591. };
  11592. AudioStreamController.prototype.onMediaDetaching = function onMediaDetaching() {
  11593. var media = this.media;
  11594. if (media && media.ended) {
  11595. logger["b" /* logger */].log('MSE detaching and video ended, reset startPosition');
  11596. this.startPosition = this.lastCurrentTime = 0;
  11597. }
  11598. // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery
  11599. var tracks = this.tracks;
  11600. if (tracks) {
  11601. // reset fragment load counter
  11602. tracks.forEach(function (track) {
  11603. if (track.details) {
  11604. track.details.fragments.forEach(function (fragment) {
  11605. fragment.loadCounter = undefined;
  11606. });
  11607. }
  11608. });
  11609. }
  11610. // remove video listeners
  11611. if (media) {
  11612. media.removeEventListener('seeking', this.onvseeking);
  11613. media.removeEventListener('ended', this.onvended);
  11614. this.onvseeking = this.onvseeked = this.onvended = null;
  11615. }
  11616. this.media = this.mediaBuffer = this.videoBuffer = null;
  11617. this.loadedmetadata = false;
  11618. this.stopLoad();
  11619. };
  11620. AudioStreamController.prototype.onMediaSeeking = function onMediaSeeking() {
  11621. if (this.state === audio_stream_controller_State.ENDED) {
  11622. // switch to IDLE state to check for potential new fragment
  11623. this.state = audio_stream_controller_State.IDLE;
  11624. }
  11625. if (this.media) {
  11626. this.lastCurrentTime = this.media.currentTime;
  11627. }
  11628. // avoid reporting fragment loop loading error in case user is seeking several times on same position
  11629. if (this.fragLoadIdx !== undefined) {
  11630. this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
  11631. }
  11632. // tick to speed up processing
  11633. this.tick();
  11634. };
  11635. AudioStreamController.prototype.onMediaEnded = function onMediaEnded() {
  11636. // reset startPosition and lastCurrentTime to restart playback @ stream beginning
  11637. this.startPosition = this.lastCurrentTime = 0;
  11638. };
  11639. AudioStreamController.prototype.onAudioTracksUpdated = function onAudioTracksUpdated(data) {
  11640. logger["b" /* logger */].log('audio tracks updated');
  11641. this.tracks = data.audioTracks;
  11642. };
  11643. AudioStreamController.prototype.onAudioTrackSwitching = function onAudioTrackSwitching(data) {
  11644. // if any URL found on new audio track, it is an alternate audio track
  11645. var altAudio = !!data.url;
  11646. this.trackId = data.id;
  11647. this.fragCurrent = null;
  11648. this.state = audio_stream_controller_State.PAUSED;
  11649. this.waitingFragment = null;
  11650. // destroy useless demuxer when switching audio to main
  11651. if (!altAudio) {
  11652. if (this.demuxer) {
  11653. this.demuxer.destroy();
  11654. this.demuxer = null;
  11655. }
  11656. } else {
  11657. // switching to audio track, start timer if not already started
  11658. this.setInterval(100);
  11659. }
  11660. //should we switch tracks ?
  11661. if (altAudio) {
  11662. this.audioSwitch = true;
  11663. //main audio track are handled by stream-controller, just do something if switching to alt audio track
  11664. this.state = audio_stream_controller_State.IDLE;
  11665. // increase fragment load Index to avoid frag loop loading error after buffer flush
  11666. if (this.fragLoadIdx !== undefined) {
  11667. this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
  11668. }
  11669. }
  11670. this.tick();
  11671. };
  11672. AudioStreamController.prototype.onAudioTrackLoaded = function onAudioTrackLoaded(data) {
  11673. var newDetails = data.details,
  11674. trackId = data.id,
  11675. track = this.tracks[trackId],
  11676. duration = newDetails.totalduration,
  11677. sliding = 0;
  11678. logger["b" /* logger */].log('track ' + trackId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
  11679. if (newDetails.live) {
  11680. var curDetails = track.details;
  11681. if (curDetails && newDetails.fragments.length > 0) {
  11682. // we already have details for that level, merge them
  11683. mergeDetails(curDetails, newDetails);
  11684. sliding = newDetails.fragments[0].start;
  11685. // TODO
  11686. //this.liveSyncPosition = this.computeLivePosition(sliding, curDetails);
  11687. if (newDetails.PTSKnown) {
  11688. logger["b" /* logger */].log('live audio playlist sliding:' + sliding.toFixed(3));
  11689. } else {
  11690. logger["b" /* logger */].log('live audio playlist - outdated PTS, unknown sliding');
  11691. }
  11692. } else {
  11693. newDetails.PTSKnown = false;
  11694. logger["b" /* logger */].log('live audio playlist - first load, unknown sliding');
  11695. }
  11696. } else {
  11697. newDetails.PTSKnown = false;
  11698. }
  11699. track.details = newDetails;
  11700. // compute start position
  11701. if (!this.startFragRequested) {
  11702. // compute start position if set to -1. use it straight away if value is defined
  11703. if (this.startPosition === -1) {
  11704. // first, check if start time offset has been set in playlist, if yes, use this value
  11705. var startTimeOffset = newDetails.startTimeOffset;
  11706. if (!isNaN(startTimeOffset)) {
  11707. logger["b" /* logger */].log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
  11708. this.startPosition = startTimeOffset;
  11709. } else {
  11710. this.startPosition = 0;
  11711. }
  11712. }
  11713. this.nextLoadPosition = this.startPosition;
  11714. }
  11715. // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment
  11716. if (this.state === audio_stream_controller_State.WAITING_TRACK) {
  11717. this.state = audio_stream_controller_State.IDLE;
  11718. }
  11719. //trigger handler right now
  11720. this.tick();
  11721. };
  11722. AudioStreamController.prototype.onKeyLoaded = function onKeyLoaded() {
  11723. if (this.state === audio_stream_controller_State.KEY_LOADING) {
  11724. this.state = audio_stream_controller_State.IDLE;
  11725. this.tick();
  11726. }
  11727. };
  11728. AudioStreamController.prototype.onFragLoaded = function onFragLoaded(data) {
  11729. var fragCurrent = this.fragCurrent,
  11730. fragLoaded = data.frag;
  11731. if (this.state === audio_stream_controller_State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'audio' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) {
  11732. var track = this.tracks[this.trackId],
  11733. details = track.details,
  11734. duration = details.totalduration,
  11735. trackId = fragCurrent.level,
  11736. sn = fragCurrent.sn,
  11737. cc = fragCurrent.cc,
  11738. audioCodec = this.config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2',
  11739. stats = this.stats = data.stats;
  11740. if (sn === 'initSegment') {
  11741. this.state = audio_stream_controller_State.IDLE;
  11742. stats.tparsed = stats.tbuffered = performance.now();
  11743. details.initSegment.data = data.payload;
  11744. this.hls.trigger(events["a" /* default */].FRAG_BUFFERED, {
  11745. stats: stats,
  11746. frag: fragCurrent,
  11747. id: 'audio'
  11748. });
  11749. this.tick();
  11750. } else {
  11751. this.state = audio_stream_controller_State.PARSING;
  11752. // transmux the MPEG-TS data to ISO-BMFF segments
  11753. this.appended = false;
  11754. if (!this.demuxer) {
  11755. this.demuxer = new demux_demuxer(this.hls, 'audio');
  11756. }
  11757. //Check if we have video initPTS
  11758. // If not we need to wait for it
  11759. var initPTS = this.initPTS[cc];
  11760. var initSegmentData = details.initSegment ? details.initSegment.data : [];
  11761. if (details.initSegment || initPTS !== undefined) {
  11762. this.pendingBuffering = true;
  11763. logger["b" /* logger */].log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],track ' + trackId);
  11764. // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)
  11765. var accurateTimeOffset = false; //details.PTSKnown || !details.live;
  11766. this.demuxer.push(data.payload, initSegmentData, audioCodec, null, fragCurrent, duration, accurateTimeOffset, initPTS);
  11767. } else {
  11768. logger["b" /* logger */].log('unknown video PTS for continuity counter ' + cc + ', waiting for video PTS before demuxing audio frag ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],track ' + trackId);
  11769. this.waitingFragment = data;
  11770. this.state = audio_stream_controller_State.WAITING_INIT_PTS;
  11771. }
  11772. }
  11773. }
  11774. this.fragLoadError = 0;
  11775. };
  11776. AudioStreamController.prototype.onFragParsingInitSegment = function onFragParsingInitSegment(data) {
  11777. var fragCurrent = this.fragCurrent;
  11778. var fragNew = data.frag;
  11779. if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === audio_stream_controller_State.PARSING) {
  11780. var tracks = data.tracks,
  11781. track = void 0;
  11782. // delete any video track found on audio demuxer
  11783. if (tracks.video) {
  11784. delete tracks.video;
  11785. }
  11786. // include levelCodec in audio and video tracks
  11787. track = tracks.audio;
  11788. if (track) {
  11789. track.levelCodec = track.codec;
  11790. track.id = data.id;
  11791. this.hls.trigger(events["a" /* default */].BUFFER_CODECS, tracks);
  11792. logger["b" /* logger */].log('audio track:audio,container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']');
  11793. var initSegment = track.initSegment;
  11794. if (initSegment) {
  11795. var appendObj = {
  11796. type: 'audio',
  11797. data: initSegment,
  11798. parent: 'audio',
  11799. content: 'initSegment'
  11800. };
  11801. if (this.audioSwitch) {
  11802. this.pendingData = [appendObj];
  11803. } else {
  11804. this.appended = true;
  11805. // arm pending Buffering flag before appending a segment
  11806. this.pendingBuffering = true;
  11807. this.hls.trigger(events["a" /* default */].BUFFER_APPENDING, appendObj);
  11808. }
  11809. }
  11810. //trigger handler right now
  11811. this.tick();
  11812. }
  11813. }
  11814. };
  11815. AudioStreamController.prototype.onFragParsingData = function onFragParsingData(data) {
  11816. var _this2 = this;
  11817. var fragCurrent = this.fragCurrent;
  11818. var fragNew = data.frag;
  11819. if (fragCurrent && data.id === 'audio' && data.type === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === audio_stream_controller_State.PARSING) {
  11820. var trackId = this.trackId,
  11821. track = this.tracks[trackId],
  11822. hls = this.hls;
  11823. if (isNaN(data.endPTS)) {
  11824. data.endPTS = data.startPTS + fragCurrent.duration;
  11825. data.endDTS = data.startDTS + fragCurrent.duration;
  11826. }
  11827. logger["b" /* logger */].log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb);
  11828. updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS);
  11829. var audioSwitch = this.audioSwitch,
  11830. media = this.media,
  11831. appendOnBufferFlush = false;
  11832. //Only flush audio from old audio tracks when PTS is known on new audio track
  11833. if (audioSwitch && media) {
  11834. if (media.readyState) {
  11835. var currentTime = media.currentTime;
  11836. logger["b" /* logger */].log('switching audio track : currentTime:' + currentTime);
  11837. if (currentTime >= data.startPTS) {
  11838. logger["b" /* logger */].log('switching audio track : flushing all audio');
  11839. this.state = audio_stream_controller_State.BUFFER_FLUSHING;
  11840. hls.trigger(events["a" /* default */].BUFFER_FLUSHING, {
  11841. startOffset: 0,
  11842. endOffset: Number.POSITIVE_INFINITY,
  11843. type: 'audio'
  11844. });
  11845. appendOnBufferFlush = true;
  11846. //Lets announce that the initial audio track switch flush occur
  11847. this.audioSwitch = false;
  11848. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHED, {id: trackId});
  11849. }
  11850. } else {
  11851. //Lets announce that the initial audio track switch flush occur
  11852. this.audioSwitch = false;
  11853. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHED, {id: trackId});
  11854. }
  11855. }
  11856. var pendingData = this.pendingData;
  11857. if (!pendingData) {
  11858. console.warn('Apparently attempt to enqueue media payload without codec initialization data upfront');
  11859. hls.trigger(events["a" /* default */].ERROR, {
  11860. type: errors["b" /* ErrorTypes */].MEDIA_ERROR,
  11861. details: null,
  11862. fatal: true
  11863. });
  11864. return;
  11865. }
  11866. if (!this.audioSwitch) {
  11867. [data.data1, data.data2].forEach(function (buffer) {
  11868. if (buffer && buffer.length) {
  11869. pendingData.push({
  11870. type: data.type,
  11871. data: buffer,
  11872. parent: 'audio',
  11873. content: 'data'
  11874. });
  11875. }
  11876. });
  11877. if (!appendOnBufferFlush && pendingData.length) {
  11878. pendingData.forEach(function (appendObj) {
  11879. // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending)
  11880. // in that case it is useless to append following segments
  11881. if (_this2.state === audio_stream_controller_State.PARSING) {
  11882. // arm pending Buffering flag before appending a segment
  11883. _this2.pendingBuffering = true;
  11884. _this2.hls.trigger(events["a" /* default */].BUFFER_APPENDING, appendObj);
  11885. }
  11886. });
  11887. this.pendingData = [];
  11888. this.appended = true;
  11889. }
  11890. }
  11891. //trigger handler right now
  11892. this.tick();
  11893. }
  11894. };
  11895. AudioStreamController.prototype.onFragParsed = function onFragParsed(data) {
  11896. var fragCurrent = this.fragCurrent;
  11897. var fragNew = data.frag;
  11898. if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === audio_stream_controller_State.PARSING) {
  11899. this.stats.tparsed = performance.now();
  11900. this.state = audio_stream_controller_State.PARSED;
  11901. this._checkAppendedParsed();
  11902. }
  11903. };
  11904. AudioStreamController.prototype.onBufferReset = function onBufferReset() {
  11905. // reset reference to sourcebuffers
  11906. this.mediaBuffer = this.videoBuffer = null;
  11907. this.loadedmetadata = false;
  11908. };
  11909. AudioStreamController.prototype.onBufferCreated = function onBufferCreated(data) {
  11910. var audioTrack = data.tracks.audio;
  11911. if (audioTrack) {
  11912. this.mediaBuffer = audioTrack.buffer;
  11913. this.loadedmetadata = true;
  11914. }
  11915. if (data.tracks.video) {
  11916. this.videoBuffer = data.tracks.video.buffer;
  11917. }
  11918. };
  11919. AudioStreamController.prototype.onBufferAppended = function onBufferAppended(data) {
  11920. if (data.parent === 'audio') {
  11921. var state = this.state;
  11922. if (state === audio_stream_controller_State.PARSING || state === audio_stream_controller_State.PARSED) {
  11923. // check if all buffers have been appended
  11924. this.pendingBuffering = data.pending > 0;
  11925. this._checkAppendedParsed();
  11926. }
  11927. }
  11928. };
  11929. AudioStreamController.prototype._checkAppendedParsed = function _checkAppendedParsed() {
  11930. //trigger handler right now
  11931. if (this.state === audio_stream_controller_State.PARSED && (!this.appended || !this.pendingBuffering)) {
  11932. var frag = this.fragCurrent,
  11933. stats = this.stats,
  11934. hls = this.hls;
  11935. if (frag) {
  11936. this.fragPrevious = frag;
  11937. stats.tbuffered = performance.now();
  11938. hls.trigger(events["a" /* default */].FRAG_BUFFERED, {
  11939. stats: stats,
  11940. frag: frag,
  11941. id: 'audio'
  11942. });
  11943. var media = this.mediaBuffer ? this.mediaBuffer : this.media;
  11944. logger["b" /* logger */].log('audio buffered : ' + timeRanges.toString(media.buffered));
  11945. if (this.audioSwitch && this.appended) {
  11946. this.audioSwitch = false;
  11947. hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHED, {id: this.trackId});
  11948. }
  11949. this.state = audio_stream_controller_State.IDLE;
  11950. }
  11951. this.tick();
  11952. }
  11953. };
  11954. AudioStreamController.prototype.onError = function onError(data) {
  11955. var frag = data.frag;
  11956. // don't handle frag error not related to audio fragment
  11957. if (frag && frag.type !== 'audio') {
  11958. return;
  11959. }
  11960. switch (data.details) {
  11961. case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
  11962. case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
  11963. if (!data.fatal) {
  11964. var loadError = this.fragLoadError;
  11965. if (loadError) {
  11966. loadError++;
  11967. } else {
  11968. loadError = 1;
  11969. }
  11970. var config = this.config;
  11971. if (loadError <= config.fragLoadingMaxRetry) {
  11972. this.fragLoadError = loadError;
  11973. // reset load counter to avoid frag loop loading error
  11974. frag.loadCounter = 0;
  11975. // exponential backoff capped to config.fragLoadingMaxRetryTimeout
  11976. var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout);
  11977. logger["b" /* logger */].warn('audioStreamController: frag loading failed, retry in ' + delay + ' ms');
  11978. this.retryDate = performance.now() + delay;
  11979. // retry loading state
  11980. this.state = audio_stream_controller_State.FRAG_LOADING_WAITING_RETRY;
  11981. } else {
  11982. logger["b" /* logger */].error('audioStreamController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
  11983. // switch error to fatal
  11984. data.fatal = true;
  11985. this.state = audio_stream_controller_State.ERROR;
  11986. }
  11987. }
  11988. break;
  11989. case errors["a" /* ErrorDetails */].FRAG_LOOP_LOADING_ERROR:
  11990. case errors["a" /* ErrorDetails */].AUDIO_TRACK_LOAD_ERROR:
  11991. case errors["a" /* ErrorDetails */].AUDIO_TRACK_LOAD_TIMEOUT:
  11992. case errors["a" /* ErrorDetails */].KEY_LOAD_ERROR:
  11993. case errors["a" /* ErrorDetails */].KEY_LOAD_TIMEOUT:
  11994. // when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received
  11995. if (this.state !== audio_stream_controller_State.ERROR) {
  11996. // if fatal error, stop processing, otherwise move to IDLE to retry loading
  11997. this.state = data.fatal ? audio_stream_controller_State.ERROR : audio_stream_controller_State.IDLE;
  11998. logger["b" /* logger */].warn('audioStreamController: ' + data.details + ' while loading frag,switch to ' + this.state + ' state ...');
  11999. }
  12000. break;
  12001. case errors["a" /* ErrorDetails */].BUFFER_FULL_ERROR:
  12002. // if in appending state
  12003. if (data.parent === 'audio' && (this.state === audio_stream_controller_State.PARSING || this.state === audio_stream_controller_State.PARSED)) {
  12004. var media = this.mediaBuffer,
  12005. currentTime = this.media.currentTime,
  12006. mediaBuffered = media && buffer_helper.isBuffered(media, currentTime) && buffer_helper.isBuffered(media, currentTime + 0.5);
  12007. // reduce max buf len if current position is buffered
  12008. if (mediaBuffered) {
  12009. var _config = this.config;
  12010. if (_config.maxMaxBufferLength >= _config.maxBufferLength) {
  12011. // reduce max buffer length as it might be too high. we do this to avoid loop flushing ...
  12012. _config.maxMaxBufferLength /= 2;
  12013. logger["b" /* logger */].warn('audio:reduce max buffer length to ' + _config.maxMaxBufferLength + 's');
  12014. // increase fragment load Index to avoid frag loop loading error after buffer flush
  12015. this.fragLoadIdx += 2 * _config.fragLoadingLoopThreshold;
  12016. }
  12017. this.state = audio_stream_controller_State.IDLE;
  12018. } else {
  12019. // current position is not buffered, but browser is still complaining about buffer full error
  12020. // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708
  12021. // in that case flush the whole audio buffer to recover
  12022. logger["b" /* logger */].warn('buffer full error also media.currentTime is not buffered, flush audio buffer');
  12023. this.fragCurrent = null;
  12024. // flush everything
  12025. this.state = audio_stream_controller_State.BUFFER_FLUSHING;
  12026. this.hls.trigger(events["a" /* default */].BUFFER_FLUSHING, {
  12027. startOffset: 0,
  12028. endOffset: Number.POSITIVE_INFINITY,
  12029. type: 'audio'
  12030. });
  12031. }
  12032. }
  12033. break;
  12034. default:
  12035. break;
  12036. }
  12037. };
  12038. AudioStreamController.prototype.onBufferFlushed = function onBufferFlushed() {
  12039. var _this3 = this;
  12040. var pendingData = this.pendingData;
  12041. if (pendingData && pendingData.length) {
  12042. logger["b" /* logger */].log('appending pending audio data on Buffer Flushed');
  12043. pendingData.forEach(function (appendObj) {
  12044. _this3.hls.trigger(events["a" /* default */].BUFFER_APPENDING, appendObj);
  12045. });
  12046. this.appended = true;
  12047. this.pendingData = [];
  12048. this.state = audio_stream_controller_State.PARSED;
  12049. } else {
  12050. // move to IDLE once flush complete. this should trigger new fragment loading
  12051. this.state = audio_stream_controller_State.IDLE;
  12052. // reset reference to frag
  12053. this.fragPrevious = null;
  12054. this.tick();
  12055. }
  12056. };
  12057. audio_stream_controller__createClass(AudioStreamController, [{
  12058. key: 'state',
  12059. set: function set(nextState) {
  12060. if (this.state !== nextState) {
  12061. var previousState = this.state;
  12062. this._state = nextState;
  12063. logger["b" /* logger */].log('audio stream:' + previousState + '->' + nextState);
  12064. }
  12065. },
  12066. get: function get() {
  12067. return this._state;
  12068. }
  12069. }]);
  12070. return AudioStreamController;
  12071. }(task_loop);
  12072. /* harmony default export */
  12073. var audio_stream_controller = (audio_stream_controller_AudioStreamController);
  12074. // CONCATENATED MODULE: ./src/utils/vttcue.js
  12075. /**
  12076. * Copyright 2013 vtt.js Contributors
  12077. *
  12078. * Licensed under the Apache License, Version 2.0 (the "License");
  12079. * you may not use this file except in compliance with the License.
  12080. * You may obtain a copy of the License at
  12081. *
  12082. * http://www.apache.org/licenses/LICENSE-2.0
  12083. *
  12084. * Unless required by applicable law or agreed to in writing, software
  12085. * distributed under the License is distributed on an "AS IS" BASIS,
  12086. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12087. * See the License for the specific language governing permissions and
  12088. * limitations under the License.
  12089. */
  12090. /* harmony default export */
  12091. var vttcue = ((function () {
  12092. if (typeof window !== 'undefined' && window.VTTCue) {
  12093. return window.VTTCue;
  12094. }
  12095. var autoKeyword = 'auto';
  12096. var directionSetting = {
  12097. '': true,
  12098. lr: true,
  12099. rl: true
  12100. };
  12101. var alignSetting = {
  12102. start: true,
  12103. middle: true,
  12104. end: true,
  12105. left: true,
  12106. right: true
  12107. };
  12108. function findDirectionSetting(value) {
  12109. if (typeof value !== 'string') {
  12110. return false;
  12111. }
  12112. var dir = directionSetting[value.toLowerCase()];
  12113. return dir ? value.toLowerCase() : false;
  12114. }
  12115. function findAlignSetting(value) {
  12116. if (typeof value !== 'string') {
  12117. return false;
  12118. }
  12119. var align = alignSetting[value.toLowerCase()];
  12120. return align ? value.toLowerCase() : false;
  12121. }
  12122. function extend(obj) {
  12123. var i = 1;
  12124. for (; i < arguments.length; i++) {
  12125. var cobj = arguments[i];
  12126. for (var p in cobj) {
  12127. obj[p] = cobj[p];
  12128. }
  12129. }
  12130. return obj;
  12131. }
  12132. function VTTCue(startTime, endTime, text) {
  12133. var cue = this;
  12134. var isIE8 = function () {
  12135. if (typeof navigator === 'undefined') {
  12136. return;
  12137. }
  12138. return (/MSIE\s8\.0/.test(navigator.userAgent)
  12139. );
  12140. }();
  12141. var baseObj = {};
  12142. if (isIE8) {
  12143. cue = document.createElement('custom');
  12144. } else {
  12145. baseObj.enumerable = true;
  12146. }
  12147. /**
  12148. * Shim implementation specific properties. These properties are not in
  12149. * the spec.
  12150. */
  12151. // Lets us know when the VTTCue's data has changed in such a way that we need
  12152. // to recompute its display state. This lets us compute its display state
  12153. // lazily.
  12154. cue.hasBeenReset = false;
  12155. /**
  12156. * VTTCue and TextTrackCue properties
  12157. * http://dev.w3.org/html5/webvtt/#vttcue-interface
  12158. */
  12159. var _id = '';
  12160. var _pauseOnExit = false;
  12161. var _startTime = startTime;
  12162. var _endTime = endTime;
  12163. var _text = text;
  12164. var _region = null;
  12165. var _vertical = '';
  12166. var _snapToLines = true;
  12167. var _line = 'auto';
  12168. var _lineAlign = 'start';
  12169. var _position = 50;
  12170. var _positionAlign = 'middle';
  12171. var _size = 50;
  12172. var _align = 'middle';
  12173. Object.defineProperty(cue, 'id', extend({}, baseObj, {
  12174. get: function get() {
  12175. return _id;
  12176. },
  12177. set: function set(value) {
  12178. _id = '' + value;
  12179. }
  12180. }));
  12181. Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, {
  12182. get: function get() {
  12183. return _pauseOnExit;
  12184. },
  12185. set: function set(value) {
  12186. _pauseOnExit = !!value;
  12187. }
  12188. }));
  12189. Object.defineProperty(cue, 'startTime', extend({}, baseObj, {
  12190. get: function get() {
  12191. return _startTime;
  12192. },
  12193. set: function set(value) {
  12194. if (typeof value !== 'number') {
  12195. throw new TypeError('Start time must be set to a number.');
  12196. }
  12197. _startTime = value;
  12198. this.hasBeenReset = true;
  12199. }
  12200. }));
  12201. Object.defineProperty(cue, 'endTime', extend({}, baseObj, {
  12202. get: function get() {
  12203. return _endTime;
  12204. },
  12205. set: function set(value) {
  12206. if (typeof value !== 'number') {
  12207. throw new TypeError('End time must be set to a number.');
  12208. }
  12209. _endTime = value;
  12210. this.hasBeenReset = true;
  12211. }
  12212. }));
  12213. Object.defineProperty(cue, 'text', extend({}, baseObj, {
  12214. get: function get() {
  12215. return _text;
  12216. },
  12217. set: function set(value) {
  12218. _text = '' + value;
  12219. this.hasBeenReset = true;
  12220. }
  12221. }));
  12222. Object.defineProperty(cue, 'region', extend({}, baseObj, {
  12223. get: function get() {
  12224. return _region;
  12225. },
  12226. set: function set(value) {
  12227. _region = value;
  12228. this.hasBeenReset = true;
  12229. }
  12230. }));
  12231. Object.defineProperty(cue, 'vertical', extend({}, baseObj, {
  12232. get: function get() {
  12233. return _vertical;
  12234. },
  12235. set: function set(value) {
  12236. var setting = findDirectionSetting(value);
  12237. // Have to check for false because the setting an be an empty string.
  12238. if (setting === false) {
  12239. throw new SyntaxError('An invalid or illegal string was specified.');
  12240. }
  12241. _vertical = setting;
  12242. this.hasBeenReset = true;
  12243. }
  12244. }));
  12245. Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, {
  12246. get: function get() {
  12247. return _snapToLines;
  12248. },
  12249. set: function set(value) {
  12250. _snapToLines = !!value;
  12251. this.hasBeenReset = true;
  12252. }
  12253. }));
  12254. Object.defineProperty(cue, 'line', extend({}, baseObj, {
  12255. get: function get() {
  12256. return _line;
  12257. },
  12258. set: function set(value) {
  12259. if (typeof value !== 'number' && value !== autoKeyword) {
  12260. throw new SyntaxError('An invalid number or illegal string was specified.');
  12261. }
  12262. _line = value;
  12263. this.hasBeenReset = true;
  12264. }
  12265. }));
  12266. Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, {
  12267. get: function get() {
  12268. return _lineAlign;
  12269. },
  12270. set: function set(value) {
  12271. var setting = findAlignSetting(value);
  12272. if (!setting) {
  12273. throw new SyntaxError('An invalid or illegal string was specified.');
  12274. }
  12275. _lineAlign = setting;
  12276. this.hasBeenReset = true;
  12277. }
  12278. }));
  12279. Object.defineProperty(cue, 'position', extend({}, baseObj, {
  12280. get: function get() {
  12281. return _position;
  12282. },
  12283. set: function set(value) {
  12284. if (value < 0 || value > 100) {
  12285. throw new Error('Position must be between 0 and 100.');
  12286. }
  12287. _position = value;
  12288. this.hasBeenReset = true;
  12289. }
  12290. }));
  12291. Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, {
  12292. get: function get() {
  12293. return _positionAlign;
  12294. },
  12295. set: function set(value) {
  12296. var setting = findAlignSetting(value);
  12297. if (!setting) {
  12298. throw new SyntaxError('An invalid or illegal string was specified.');
  12299. }
  12300. _positionAlign = setting;
  12301. this.hasBeenReset = true;
  12302. }
  12303. }));
  12304. Object.defineProperty(cue, 'size', extend({}, baseObj, {
  12305. get: function get() {
  12306. return _size;
  12307. },
  12308. set: function set(value) {
  12309. if (value < 0 || value > 100) {
  12310. throw new Error('Size must be between 0 and 100.');
  12311. }
  12312. _size = value;
  12313. this.hasBeenReset = true;
  12314. }
  12315. }));
  12316. Object.defineProperty(cue, 'align', extend({}, baseObj, {
  12317. get: function get() {
  12318. return _align;
  12319. },
  12320. set: function set(value) {
  12321. var setting = findAlignSetting(value);
  12322. if (!setting) {
  12323. throw new SyntaxError('An invalid or illegal string was specified.');
  12324. }
  12325. _align = setting;
  12326. this.hasBeenReset = true;
  12327. }
  12328. }));
  12329. /**
  12330. * Other <track> spec defined properties
  12331. */
  12332. // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
  12333. cue.displayState = undefined;
  12334. if (isIE8) {
  12335. return cue;
  12336. }
  12337. }
  12338. /**
  12339. * VTTCue methods
  12340. */
  12341. VTTCue.prototype.getCueAsHTML = function () {
  12342. // Assume WebVTT.convertCueToDOMTree is on the global.
  12343. var WebVTT = window.WebVTT;
  12344. return WebVTT.convertCueToDOMTree(window, this.text);
  12345. };
  12346. return VTTCue;
  12347. })());
  12348. // CONCATENATED MODULE: ./src/utils/vttparser.js
  12349. /*
  12350. * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js#L1716
  12351. */
  12352. var StringDecoder = function StringDecoder() {
  12353. return {
  12354. decode: function decode(data) {
  12355. if (!data) {
  12356. return '';
  12357. }
  12358. if (typeof data !== 'string') {
  12359. throw new Error('Error - expected string data.');
  12360. }
  12361. return decodeURIComponent(encodeURIComponent(data));
  12362. }
  12363. };
  12364. };
  12365. function VTTParser() {
  12366. this.window = window;
  12367. this.state = 'INITIAL';
  12368. this.buffer = '';
  12369. this.decoder = new StringDecoder();
  12370. this.regionList = [];
  12371. }
  12372. // Try to parse input as a time stamp.
  12373. function parseTimeStamp(input) {
  12374. function computeSeconds(h, m, s, f) {
  12375. return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
  12376. }
  12377. var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
  12378. if (!m) {
  12379. return null;
  12380. }
  12381. if (m[3]) {
  12382. // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
  12383. return computeSeconds(m[1], m[2], m[3].replace(':', ''), m[4]);
  12384. } else if (m[1] > 59) {
  12385. // Timestamp takes the form of [hours]:[minutes].[milliseconds]
  12386. // First position is hours as it's over 59.
  12387. return computeSeconds(m[1], m[2], 0, m[4]);
  12388. } else {
  12389. // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
  12390. return computeSeconds(0, m[1], m[2], m[4]);
  12391. }
  12392. }
  12393. // A settings object holds key/value pairs and will ignore anything but the first
  12394. // assignment to a specific key.
  12395. function Settings() {
  12396. this.values = Object.create(null);
  12397. }
  12398. Settings.prototype = {
  12399. // Only accept the first assignment to any key.
  12400. set: function set(k, v) {
  12401. if (!this.get(k) && v !== '') {
  12402. this.values[k] = v;
  12403. }
  12404. },
  12405. // Return the value for a key, or a default value.
  12406. // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
  12407. // a number of possible default values as properties where 'defaultKey' is
  12408. // the key of the property that will be chosen; otherwise it's assumed to be
  12409. // a single value.
  12410. get: function get(k, dflt, defaultKey) {
  12411. if (defaultKey) {
  12412. return this.has(k) ? this.values[k] : dflt[defaultKey];
  12413. }
  12414. return this.has(k) ? this.values[k] : dflt;
  12415. },
  12416. // Check whether we have a value for a key.
  12417. has: function has(k) {
  12418. return k in this.values;
  12419. },
  12420. // Accept a setting if its one of the given alternatives.
  12421. alt: function alt(k, v, a) {
  12422. for (var n = 0; n < a.length; ++n) {
  12423. if (v === a[n]) {
  12424. this.set(k, v);
  12425. break;
  12426. }
  12427. }
  12428. },
  12429. // Accept a setting if its a valid (signed) integer.
  12430. integer: function integer(k, v) {
  12431. if (/^-?\d+$/.test(v)) {
  12432. // integer
  12433. this.set(k, parseInt(v, 10));
  12434. }
  12435. },
  12436. // Accept a setting if its a valid percentage.
  12437. percent: function percent(k, v) {
  12438. var m;
  12439. if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
  12440. v = parseFloat(v);
  12441. if (v >= 0 && v <= 100) {
  12442. this.set(k, v);
  12443. return true;
  12444. }
  12445. }
  12446. return false;
  12447. }
  12448. };
  12449. // Helper function to parse input into groups separated by 'groupDelim', and
  12450. // interprete each group as a key/value pair separated by 'keyValueDelim'.
  12451. function parseOptions(input, callback, keyValueDelim, groupDelim) {
  12452. var groups = groupDelim ? input.split(groupDelim) : [input];
  12453. for (var i in groups) {
  12454. if (typeof groups[i] !== 'string') {
  12455. continue;
  12456. }
  12457. var kv = groups[i].split(keyValueDelim);
  12458. if (kv.length !== 2) {
  12459. continue;
  12460. }
  12461. var k = kv[0];
  12462. var v = kv[1];
  12463. callback(k, v);
  12464. }
  12465. }
  12466. var defaults = new vttcue(0, 0, 0);
  12467. // 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244
  12468. // Safari doesn't yet support this change, but FF and Chrome do.
  12469. var center = defaults.align === 'middle' ? 'middle' : 'center';
  12470. function parseCue(input, cue, regionList) {
  12471. // Remember the original input if we need to throw an error.
  12472. var oInput = input;
  12473. // 4.1 WebVTT timestamp
  12474. function consumeTimeStamp() {
  12475. var ts = parseTimeStamp(input);
  12476. if (ts === null) {
  12477. throw new Error('Malformed timestamp: ' + oInput);
  12478. }
  12479. // Remove time stamp from input.
  12480. input = input.replace(/^[^\sa-zA-Z-]+/, '');
  12481. return ts;
  12482. }
  12483. // 4.4.2 WebVTT cue settings
  12484. function consumeCueSettings(input, cue) {
  12485. var settings = new Settings();
  12486. parseOptions(input, function (k, v) {
  12487. switch (k) {
  12488. case 'region':
  12489. // Find the last region we parsed with the same region id.
  12490. for (var i = regionList.length - 1; i >= 0; i--) {
  12491. if (regionList[i].id === v) {
  12492. settings.set(k, regionList[i].region);
  12493. break;
  12494. }
  12495. }
  12496. break;
  12497. case 'vertical':
  12498. settings.alt(k, v, ['rl', 'lr']);
  12499. break;
  12500. case 'line':
  12501. var vals = v.split(','),
  12502. vals0 = vals[0];
  12503. settings.integer(k, vals0);
  12504. if (settings.percent(k, vals0)) {
  12505. settings.set('snapToLines', false);
  12506. }
  12507. settings.alt(k, vals0, ['auto']);
  12508. if (vals.length === 2) {
  12509. settings.alt('lineAlign', vals[1], ['start', center, 'end']);
  12510. }
  12511. break;
  12512. case 'position':
  12513. vals = v.split(',');
  12514. settings.percent(k, vals[0]);
  12515. if (vals.length === 2) {
  12516. settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']);
  12517. }
  12518. break;
  12519. case 'size':
  12520. settings.percent(k, v);
  12521. break;
  12522. case 'align':
  12523. settings.alt(k, v, ['start', center, 'end', 'left', 'right']);
  12524. break;
  12525. }
  12526. }, /:/, /\s/);
  12527. // Apply default values for any missing fields.
  12528. cue.region = settings.get('region', null);
  12529. cue.vertical = settings.get('vertical', '');
  12530. var line = settings.get('line', 'auto');
  12531. if (line === 'auto' && defaults.line === -1) {
  12532. // set numeric line number for Safari
  12533. line = -1;
  12534. }
  12535. cue.line = line;
  12536. cue.lineAlign = settings.get('lineAlign', 'start');
  12537. cue.snapToLines = settings.get('snapToLines', true);
  12538. cue.size = settings.get('size', 100);
  12539. cue.align = settings.get('align', center);
  12540. var position = settings.get('position', 'auto');
  12541. if (position === 'auto' && defaults.position === 50) {
  12542. // set numeric position for Safari
  12543. position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50;
  12544. }
  12545. cue.position = position;
  12546. }
  12547. function skipWhitespace() {
  12548. input = input.replace(/^\s+/, '');
  12549. }
  12550. // 4.1 WebVTT cue timings.
  12551. skipWhitespace();
  12552. cue.startTime = consumeTimeStamp(); // (1) collect cue start time
  12553. skipWhitespace();
  12554. if (input.substr(0, 3) !== '-->') {
  12555. // (3) next characters must match '-->'
  12556. throw new Error('Malformed time stamp (time stamps must be separated by \'-->\'): ' + oInput);
  12557. }
  12558. input = input.substr(3);
  12559. skipWhitespace();
  12560. cue.endTime = consumeTimeStamp(); // (5) collect cue end time
  12561. // 4.1 WebVTT cue settings list.
  12562. skipWhitespace();
  12563. consumeCueSettings(input, cue);
  12564. }
  12565. function fixLineBreaks(input) {
  12566. return input.replace(/<br(?: \/)?>/gi, '\n');
  12567. }
  12568. VTTParser.prototype = {
  12569. parse: function parse(data) {
  12570. var self = this;
  12571. // If there is no data then we won't decode it, but will just try to parse
  12572. // whatever is in buffer already. This may occur in circumstances, for
  12573. // example when flush() is called.
  12574. if (data) {
  12575. // Try to decode the data that we received.
  12576. self.buffer += self.decoder.decode(data, {stream: true});
  12577. }
  12578. function collectNextLine() {
  12579. var buffer = self.buffer;
  12580. var pos = 0;
  12581. buffer = fixLineBreaks(buffer);
  12582. while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
  12583. ++pos;
  12584. }
  12585. var line = buffer.substr(0, pos);
  12586. // Advance the buffer early in case we fail below.
  12587. if (buffer[pos] === '\r') {
  12588. ++pos;
  12589. }
  12590. if (buffer[pos] === '\n') {
  12591. ++pos;
  12592. }
  12593. self.buffer = buffer.substr(pos);
  12594. return line;
  12595. }
  12596. // 3.2 WebVTT metadata header syntax
  12597. function parseHeader(input) {
  12598. parseOptions(input, function (k, v) {
  12599. switch (k) {
  12600. case 'Region':
  12601. // 3.3 WebVTT region metadata header syntax
  12602. console.log('parse region', v);
  12603. //parseRegion(v);
  12604. break;
  12605. }
  12606. }, /:/);
  12607. }
  12608. // 5.1 WebVTT file parsing.
  12609. try {
  12610. var line;
  12611. if (self.state === 'INITIAL') {
  12612. // We can't start parsing until we have the first line.
  12613. if (!/\r\n|\n/.test(self.buffer)) {
  12614. return this;
  12615. }
  12616. line = collectNextLine();
  12617. // strip of UTF-8 BOM if any
  12618. // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
  12619. var m = line.match(/^()?WEBVTT([ \t].*)?$/);
  12620. if (!m || !m[0]) {
  12621. throw new Error('Malformed WebVTT signature.');
  12622. }
  12623. self.state = 'HEADER';
  12624. }
  12625. var alreadyCollectedLine = false;
  12626. while (self.buffer) {
  12627. // We can't parse a line until we have the full line.
  12628. if (!/\r\n|\n/.test(self.buffer)) {
  12629. return this;
  12630. }
  12631. if (!alreadyCollectedLine) {
  12632. line = collectNextLine();
  12633. } else {
  12634. alreadyCollectedLine = false;
  12635. }
  12636. switch (self.state) {
  12637. case 'HEADER':
  12638. // 13-18 - Allow a header (metadata) under the WEBVTT line.
  12639. if (/:/.test(line)) {
  12640. parseHeader(line);
  12641. } else if (!line) {
  12642. // An empty line terminates the header and starts the body (cues).
  12643. self.state = 'ID';
  12644. }
  12645. continue;
  12646. case 'NOTE':
  12647. // Ignore NOTE blocks.
  12648. if (!line) {
  12649. self.state = 'ID';
  12650. }
  12651. continue;
  12652. case 'ID':
  12653. // Check for the start of NOTE blocks.
  12654. if (/^NOTE($|[ \t])/.test(line)) {
  12655. self.state = 'NOTE';
  12656. break;
  12657. }
  12658. // 19-29 - Allow any number of line terminators, then initialize new cue values.
  12659. if (!line) {
  12660. continue;
  12661. }
  12662. self.cue = new vttcue(0, 0, '');
  12663. self.state = 'CUE';
  12664. // 30-39 - Check if self line contains an optional identifier or timing data.
  12665. if (line.indexOf('-->') === -1) {
  12666. self.cue.id = line;
  12667. continue;
  12668. }
  12669. // Process line as start of a cue.
  12670. /*falls through*/
  12671. case 'CUE':
  12672. // 40 - Collect cue timings and settings.
  12673. try {
  12674. parseCue(line, self.cue, self.regionList);
  12675. } catch (e) {
  12676. // In case of an error ignore rest of the cue.
  12677. self.cue = null;
  12678. self.state = 'BADCUE';
  12679. continue;
  12680. }
  12681. self.state = 'CUETEXT';
  12682. continue;
  12683. case 'CUETEXT':
  12684. var hasSubstring = line.indexOf('-->') !== -1;
  12685. // 34 - If we have an empty line then report the cue.
  12686. // 35 - If we have the special substring '-->' then report the cue,
  12687. // but do not collect the line as we need to process the current
  12688. // one as a new cue.
  12689. if (!line || hasSubstring && (alreadyCollectedLine = true)) {
  12690. // We are done parsing self cue.
  12691. if (self.oncue) {
  12692. self.oncue(self.cue);
  12693. }
  12694. self.cue = null;
  12695. self.state = 'ID';
  12696. continue;
  12697. }
  12698. if (self.cue.text) {
  12699. self.cue.text += '\n';
  12700. }
  12701. self.cue.text += line;
  12702. continue;
  12703. case 'BADCUE':
  12704. // BADCUE
  12705. // 54-62 - Collect and discard the remaining cue.
  12706. if (!line) {
  12707. self.state = 'ID';
  12708. }
  12709. continue;
  12710. }
  12711. }
  12712. } catch (e) {
  12713. // If we are currently parsing a cue, report what we have.
  12714. if (self.state === 'CUETEXT' && self.cue && self.oncue) {
  12715. self.oncue(self.cue);
  12716. }
  12717. self.cue = null;
  12718. // Enter BADWEBVTT state if header was not parsed correctly otherwise
  12719. // another exception occurred so enter BADCUE state.
  12720. self.state = self.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE';
  12721. }
  12722. return this;
  12723. },
  12724. flush: function flush() {
  12725. var self = this;
  12726. try {
  12727. // Finish decoding the stream.
  12728. self.buffer += self.decoder.decode();
  12729. // Synthesize the end of the current cue or region.
  12730. if (self.cue || self.state === 'HEADER') {
  12731. self.buffer += '\n\n';
  12732. self.parse();
  12733. }
  12734. // If we've flushed, parsed, and we're still on the INITIAL state then
  12735. // that means we don't have enough of the stream to parse the first
  12736. // line.
  12737. if (self.state === 'INITIAL') {
  12738. throw new Error('Malformed WebVTT signature.');
  12739. }
  12740. } catch (e) {
  12741. throw e;
  12742. }
  12743. if (self.onflush) {
  12744. self.onflush();
  12745. }
  12746. return this;
  12747. }
  12748. };
  12749. /* harmony default export */
  12750. var vttparser = (VTTParser);
  12751. // CONCATENATED MODULE: ./src/utils/cues.js
  12752. function newCue(track, startTime, endTime, captionScreen) {
  12753. var row;
  12754. var cue;
  12755. var indenting;
  12756. var indent;
  12757. var text;
  12758. var VTTCue = window.VTTCue || window.TextTrackCue;
  12759. for (var r = 0; r < captionScreen.rows.length; r++) {
  12760. row = captionScreen.rows[r];
  12761. indenting = true;
  12762. indent = 0;
  12763. text = '';
  12764. if (!row.isEmpty()) {
  12765. for (var c = 0; c < row.chars.length; c++) {
  12766. if (row.chars[c].uchar.match(/\s/) && indenting) {
  12767. indent++;
  12768. } else {
  12769. text += row.chars[c].uchar;
  12770. indenting = false;
  12771. }
  12772. }
  12773. //To be used for cleaning-up orphaned roll-up captions
  12774. row.cueStartTime = startTime;
  12775. // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
  12776. if (startTime === endTime) {
  12777. endTime += 0.0001;
  12778. }
  12779. cue = new VTTCue(startTime, endTime, fixLineBreaks(text.trim()));
  12780. if (indent >= 16) {
  12781. indent--;
  12782. } else {
  12783. indent++;
  12784. }
  12785. // VTTCue.line get's flakey when using controls, so let's now include line 13&14
  12786. // also, drop line 1 since it's to close to the top
  12787. if (navigator.userAgent.match(/Firefox\//)) {
  12788. cue.line = r + 1;
  12789. } else {
  12790. cue.line = r > 7 ? r - 2 : r + 1;
  12791. }
  12792. cue.align = 'left';
  12793. // Clamp the position between 0 and 100 - if out of these bounds, Firefox throws an exception and captions break
  12794. cue.position = Math.max(0, Math.min(100, 100 * (indent / 32) + (navigator.userAgent.match(/Firefox\//) ? 50 : 0)));
  12795. track.addCue(cue);
  12796. }
  12797. }
  12798. }
  12799. // CONCATENATED MODULE: ./src/utils/cea-608-parser.js
  12800. function cea_608_parser__classCallCheck(instance, Constructor) {
  12801. if (!(instance instanceof Constructor)) {
  12802. throw new TypeError("Cannot call a class as a function");
  12803. }
  12804. }
  12805. /**
  12806. *
  12807. * This code was ported from the dash.js project at:
  12808. * https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js
  12809. * https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2
  12810. *
  12811. * The original copyright appears below:
  12812. *
  12813. * The copyright in this software is being made available under the BSD License,
  12814. * included below. This software may be subject to other third party and contributor
  12815. * rights, including patent rights, and no such rights are granted under this license.
  12816. *
  12817. * Copyright (c) 2015-2016, DASH Industry Forum.
  12818. * All rights reserved.
  12819. *
  12820. * Redistribution and use in source and binary forms, with or without modification,
  12821. * are permitted provided that the following conditions are met:
  12822. * 1. Redistributions of source code must retain the above copyright notice, this
  12823. * list of conditions and the following disclaimer.
  12824. * * Redistributions in binary form must reproduce the above copyright notice,
  12825. * this list of conditions and the following disclaimer in the documentation and/or
  12826. * other materials provided with the distribution.
  12827. * 2. Neither the name of Dash Industry Forum nor the names of its
  12828. * contributors may be used to endorse or promote products derived from this software
  12829. * without specific prior written permission.
  12830. *
  12831. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
  12832. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  12833. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  12834. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  12835. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  12836. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  12837. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  12838. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  12839. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  12840. * POSSIBILITY OF SUCH DAMAGE.
  12841. */
  12842. /**
  12843. * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes
  12844. */
  12845. var specialCea608CharsCodes = {
  12846. 0x2a: 0xe1, // lowercase a, acute accent
  12847. 0x5c: 0xe9, // lowercase e, acute accent
  12848. 0x5e: 0xed, // lowercase i, acute accent
  12849. 0x5f: 0xf3, // lowercase o, acute accent
  12850. 0x60: 0xfa, // lowercase u, acute accent
  12851. 0x7b: 0xe7, // lowercase c with cedilla
  12852. 0x7c: 0xf7, // division symbol
  12853. 0x7d: 0xd1, // uppercase N tilde
  12854. 0x7e: 0xf1, // lowercase n tilde
  12855. 0x7f: 0x2588, // Full block
  12856. // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
  12857. // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
  12858. // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES
  12859. 0x80: 0xae, // Registered symbol (R)
  12860. 0x81: 0xb0, // degree sign
  12861. 0x82: 0xbd, // 1/2 symbol
  12862. 0x83: 0xbf, // Inverted (open) question mark
  12863. 0x84: 0x2122, // Trademark symbol (TM)
  12864. 0x85: 0xa2, // Cents symbol
  12865. 0x86: 0xa3, // Pounds sterling
  12866. 0x87: 0x266a, // Music 8'th note
  12867. 0x88: 0xe0, // lowercase a, grave accent
  12868. 0x89: 0x20, // transparent space (regular)
  12869. 0x8a: 0xe8, // lowercase e, grave accent
  12870. 0x8b: 0xe2, // lowercase a, circumflex accent
  12871. 0x8c: 0xea, // lowercase e, circumflex accent
  12872. 0x8d: 0xee, // lowercase i, circumflex accent
  12873. 0x8e: 0xf4, // lowercase o, circumflex accent
  12874. 0x8f: 0xfb, // lowercase u, circumflex accent
  12875. // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
  12876. // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
  12877. 0x90: 0xc1, // capital letter A with acute
  12878. 0x91: 0xc9, // capital letter E with acute
  12879. 0x92: 0xd3, // capital letter O with acute
  12880. 0x93: 0xda, // capital letter U with acute
  12881. 0x94: 0xdc, // capital letter U with diaresis
  12882. 0x95: 0xfc, // lowercase letter U with diaeresis
  12883. 0x96: 0x2018, // opening single quote
  12884. 0x97: 0xa1, // inverted exclamation mark
  12885. 0x98: 0x2a, // asterisk
  12886. 0x99: 0x2019, // closing single quote
  12887. 0x9a: 0x2501, // box drawings heavy horizontal
  12888. 0x9b: 0xa9, // copyright sign
  12889. 0x9c: 0x2120, // Service mark
  12890. 0x9d: 0x2022, // (round) bullet
  12891. 0x9e: 0x201c, // Left double quotation mark
  12892. 0x9f: 0x201d, // Right double quotation mark
  12893. 0xa0: 0xc0, // uppercase A, grave accent
  12894. 0xa1: 0xc2, // uppercase A, circumflex
  12895. 0xa2: 0xc7, // uppercase C with cedilla
  12896. 0xa3: 0xc8, // uppercase E, grave accent
  12897. 0xa4: 0xca, // uppercase E, circumflex
  12898. 0xa5: 0xcb, // capital letter E with diaresis
  12899. 0xa6: 0xeb, // lowercase letter e with diaresis
  12900. 0xa7: 0xce, // uppercase I, circumflex
  12901. 0xa8: 0xcf, // uppercase I, with diaresis
  12902. 0xa9: 0xef, // lowercase i, with diaresis
  12903. 0xaa: 0xd4, // uppercase O, circumflex
  12904. 0xab: 0xd9, // uppercase U, grave accent
  12905. 0xac: 0xf9, // lowercase u, grave accent
  12906. 0xad: 0xdb, // uppercase U, circumflex
  12907. 0xae: 0xab, // left-pointing double angle quotation mark
  12908. 0xaf: 0xbb, // right-pointing double angle quotation mark
  12909. // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
  12910. // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
  12911. 0xb0: 0xc3, // Uppercase A, tilde
  12912. 0xb1: 0xe3, // Lowercase a, tilde
  12913. 0xb2: 0xcd, // Uppercase I, acute accent
  12914. 0xb3: 0xcc, // Uppercase I, grave accent
  12915. 0xb4: 0xec, // Lowercase i, grave accent
  12916. 0xb5: 0xd2, // Uppercase O, grave accent
  12917. 0xb6: 0xf2, // Lowercase o, grave accent
  12918. 0xb7: 0xd5, // Uppercase O, tilde
  12919. 0xb8: 0xf5, // Lowercase o, tilde
  12920. 0xb9: 0x7b, // Open curly brace
  12921. 0xba: 0x7d, // Closing curly brace
  12922. 0xbb: 0x5c, // Backslash
  12923. 0xbc: 0x5e, // Caret
  12924. 0xbd: 0x5f, // Underscore
  12925. 0xbe: 0x7c, // Pipe (vertical line)
  12926. 0xbf: 0x223c, // Tilde operator
  12927. 0xc0: 0xc4, // Uppercase A, umlaut
  12928. 0xc1: 0xe4, // Lowercase A, umlaut
  12929. 0xc2: 0xd6, // Uppercase O, umlaut
  12930. 0xc3: 0xf6, // Lowercase o, umlaut
  12931. 0xc4: 0xdf, // Esszett (sharp S)
  12932. 0xc5: 0xa5, // Yen symbol
  12933. 0xc6: 0xa4, // Generic currency sign
  12934. 0xc7: 0x2503, // Box drawings heavy vertical
  12935. 0xc8: 0xc5, // Uppercase A, ring
  12936. 0xc9: 0xe5, // Lowercase A, ring
  12937. 0xca: 0xd8, // Uppercase O, stroke
  12938. 0xcb: 0xf8, // Lowercase o, strok
  12939. 0xcc: 0x250f, // Box drawings heavy down and right
  12940. 0xcd: 0x2513, // Box drawings heavy down and left
  12941. 0xce: 0x2517, // Box drawings heavy up and right
  12942. 0xcf: 0x251b // Box drawings heavy up and left
  12943. };
  12944. /**
  12945. * Utils
  12946. */
  12947. var getCharForByte = function getCharForByte(byte) {
  12948. var charCode = byte;
  12949. if (specialCea608CharsCodes.hasOwnProperty(byte)) {
  12950. charCode = specialCea608CharsCodes[byte];
  12951. }
  12952. return String.fromCharCode(charCode);
  12953. };
  12954. var NR_ROWS = 15,
  12955. NR_COLS = 100;
  12956. // Tables to look up row from PAC data
  12957. var rowsLowCh1 = {0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14};
  12958. var rowsHighCh1 = {0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15};
  12959. var rowsLowCh2 = {0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14};
  12960. var rowsHighCh2 = {0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15};
  12961. var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent'];
  12962. /**
  12963. * Simple logger class to be able to write with time-stamps and filter on level.
  12964. */
  12965. var cea_608_parser_logger = {
  12966. verboseFilter: {'DATA': 3, 'DEBUG': 3, 'INFO': 2, 'WARNING': 2, 'TEXT': 1, 'ERROR': 0},
  12967. time: null,
  12968. verboseLevel: 0, // Only write errors
  12969. setTime: function setTime(newTime) {
  12970. this.time = newTime;
  12971. },
  12972. log: function log(severity, msg) {
  12973. var minLevel = this.verboseFilter[severity];
  12974. if (this.verboseLevel >= minLevel) {
  12975. console.log(this.time + ' [' + severity + '] ' + msg);
  12976. }
  12977. }
  12978. };
  12979. var numArrayToHexArray = function numArrayToHexArray(numArray) {
  12980. var hexArray = [];
  12981. for (var j = 0; j < numArray.length; j++) {
  12982. hexArray.push(numArray[j].toString(16));
  12983. }
  12984. return hexArray;
  12985. };
  12986. var PenState = function () {
  12987. function PenState(foreground, underline, italics, background, flash) {
  12988. cea_608_parser__classCallCheck(this, PenState);
  12989. this.foreground = foreground || 'white';
  12990. this.underline = underline || false;
  12991. this.italics = italics || false;
  12992. this.background = background || 'black';
  12993. this.flash = flash || false;
  12994. }
  12995. PenState.prototype.reset = function reset() {
  12996. this.foreground = 'white';
  12997. this.underline = false;
  12998. this.italics = false;
  12999. this.background = 'black';
  13000. this.flash = false;
  13001. };
  13002. PenState.prototype.setStyles = function setStyles(styles) {
  13003. var attribs = ['foreground', 'underline', 'italics', 'background', 'flash'];
  13004. for (var i = 0; i < attribs.length; i++) {
  13005. var style = attribs[i];
  13006. if (styles.hasOwnProperty(style)) {
  13007. this[style] = styles[style];
  13008. }
  13009. }
  13010. };
  13011. PenState.prototype.isDefault = function isDefault() {
  13012. return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash;
  13013. };
  13014. PenState.prototype.equals = function equals(other) {
  13015. return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash;
  13016. };
  13017. PenState.prototype.copy = function copy(newPenState) {
  13018. this.foreground = newPenState.foreground;
  13019. this.underline = newPenState.underline;
  13020. this.italics = newPenState.italics;
  13021. this.background = newPenState.background;
  13022. this.flash = newPenState.flash;
  13023. };
  13024. PenState.prototype.toString = function toString() {
  13025. return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash;
  13026. };
  13027. return PenState;
  13028. }();
  13029. /**
  13030. * Unicode character with styling and background.
  13031. * @constructor
  13032. */
  13033. var StyledUnicodeChar = function () {
  13034. function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) {
  13035. cea_608_parser__classCallCheck(this, StyledUnicodeChar);
  13036. this.uchar = uchar || ' '; // unicode character
  13037. this.penState = new PenState(foreground, underline, italics, background, flash);
  13038. }
  13039. StyledUnicodeChar.prototype.reset = function reset() {
  13040. this.uchar = ' ';
  13041. this.penState.reset();
  13042. };
  13043. StyledUnicodeChar.prototype.setChar = function setChar(uchar, newPenState) {
  13044. this.uchar = uchar;
  13045. this.penState.copy(newPenState);
  13046. };
  13047. StyledUnicodeChar.prototype.setPenState = function setPenState(newPenState) {
  13048. this.penState.copy(newPenState);
  13049. };
  13050. StyledUnicodeChar.prototype.equals = function equals(other) {
  13051. return this.uchar === other.uchar && this.penState.equals(other.penState);
  13052. };
  13053. StyledUnicodeChar.prototype.copy = function copy(newChar) {
  13054. this.uchar = newChar.uchar;
  13055. this.penState.copy(newChar.penState);
  13056. };
  13057. StyledUnicodeChar.prototype.isEmpty = function isEmpty() {
  13058. return this.uchar === ' ' && this.penState.isDefault();
  13059. };
  13060. return StyledUnicodeChar;
  13061. }();
  13062. /**
  13063. * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar.
  13064. * @constructor
  13065. */
  13066. var Row = function () {
  13067. function Row() {
  13068. cea_608_parser__classCallCheck(this, Row);
  13069. this.chars = [];
  13070. for (var i = 0; i < NR_COLS; i++) {
  13071. this.chars.push(new StyledUnicodeChar());
  13072. }
  13073. this.pos = 0;
  13074. this.currPenState = new PenState();
  13075. }
  13076. Row.prototype.equals = function equals(other) {
  13077. var equal = true;
  13078. for (var i = 0; i < NR_COLS; i++) {
  13079. if (!this.chars[i].equals(other.chars[i])) {
  13080. equal = false;
  13081. break;
  13082. }
  13083. }
  13084. return equal;
  13085. };
  13086. Row.prototype.copy = function copy(other) {
  13087. for (var i = 0; i < NR_COLS; i++) {
  13088. this.chars[i].copy(other.chars[i]);
  13089. }
  13090. };
  13091. Row.prototype.isEmpty = function isEmpty() {
  13092. var empty = true;
  13093. for (var i = 0; i < NR_COLS; i++) {
  13094. if (!this.chars[i].isEmpty()) {
  13095. empty = false;
  13096. break;
  13097. }
  13098. }
  13099. return empty;
  13100. };
  13101. /**
  13102. * Set the cursor to a valid column.
  13103. */
  13104. Row.prototype.setCursor = function setCursor(absPos) {
  13105. if (this.pos !== absPos) {
  13106. this.pos = absPos;
  13107. }
  13108. if (this.pos < 0) {
  13109. cea_608_parser_logger.log('ERROR', 'Negative cursor position ' + this.pos);
  13110. this.pos = 0;
  13111. } else if (this.pos > NR_COLS) {
  13112. cea_608_parser_logger.log('ERROR', 'Too large cursor position ' + this.pos);
  13113. this.pos = NR_COLS;
  13114. }
  13115. };
  13116. /**
  13117. * Move the cursor relative to current position.
  13118. */
  13119. Row.prototype.moveCursor = function moveCursor(relPos) {
  13120. var newPos = this.pos + relPos;
  13121. if (relPos > 1) {
  13122. for (var i = this.pos + 1; i < newPos + 1; i++) {
  13123. this.chars[i].setPenState(this.currPenState);
  13124. }
  13125. }
  13126. this.setCursor(newPos);
  13127. };
  13128. /**
  13129. * Backspace, move one step back and clear character.
  13130. */
  13131. Row.prototype.backSpace = function backSpace() {
  13132. this.moveCursor(-1);
  13133. this.chars[this.pos].setChar(' ', this.currPenState);
  13134. };
  13135. Row.prototype.insertChar = function insertChar(byte) {
  13136. if (byte >= 0x90) {
  13137. //Extended char
  13138. this.backSpace();
  13139. }
  13140. var char = getCharForByte(byte);
  13141. if (this.pos >= NR_COLS) {
  13142. cea_608_parser_logger.log('ERROR', 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!');
  13143. return;
  13144. }
  13145. this.chars[this.pos].setChar(char, this.currPenState);
  13146. this.moveCursor(1);
  13147. };
  13148. Row.prototype.clearFromPos = function clearFromPos(startPos) {
  13149. var i;
  13150. for (i = startPos; i < NR_COLS; i++) {
  13151. this.chars[i].reset();
  13152. }
  13153. };
  13154. Row.prototype.clear = function clear() {
  13155. this.clearFromPos(0);
  13156. this.pos = 0;
  13157. this.currPenState.reset();
  13158. };
  13159. Row.prototype.clearToEndOfRow = function clearToEndOfRow() {
  13160. this.clearFromPos(this.pos);
  13161. };
  13162. Row.prototype.getTextString = function getTextString() {
  13163. var chars = [];
  13164. var empty = true;
  13165. for (var i = 0; i < NR_COLS; i++) {
  13166. var char = this.chars[i].uchar;
  13167. if (char !== ' ') {
  13168. empty = false;
  13169. }
  13170. chars.push(char);
  13171. }
  13172. if (empty) {
  13173. return '';
  13174. } else {
  13175. return chars.join('');
  13176. }
  13177. };
  13178. Row.prototype.setPenStyles = function setPenStyles(styles) {
  13179. this.currPenState.setStyles(styles);
  13180. var currChar = this.chars[this.pos];
  13181. currChar.setPenState(this.currPenState);
  13182. };
  13183. return Row;
  13184. }();
  13185. /**
  13186. * Keep a CEA-608 screen of 32x15 styled characters
  13187. * @constructor
  13188. */
  13189. var CaptionScreen = function () {
  13190. function CaptionScreen() {
  13191. cea_608_parser__classCallCheck(this, CaptionScreen);
  13192. this.rows = [];
  13193. for (var i = 0; i < NR_ROWS; i++) {
  13194. this.rows.push(new Row()); // Note that we use zero-based numbering (0-14)
  13195. }
  13196. this.currRow = NR_ROWS - 1;
  13197. this.nrRollUpRows = null;
  13198. this.reset();
  13199. }
  13200. CaptionScreen.prototype.reset = function reset() {
  13201. for (var i = 0; i < NR_ROWS; i++) {
  13202. this.rows[i].clear();
  13203. }
  13204. this.currRow = NR_ROWS - 1;
  13205. };
  13206. CaptionScreen.prototype.equals = function equals(other) {
  13207. var equal = true;
  13208. for (var i = 0; i < NR_ROWS; i++) {
  13209. if (!this.rows[i].equals(other.rows[i])) {
  13210. equal = false;
  13211. break;
  13212. }
  13213. }
  13214. return equal;
  13215. };
  13216. CaptionScreen.prototype.copy = function copy(other) {
  13217. for (var i = 0; i < NR_ROWS; i++) {
  13218. this.rows[i].copy(other.rows[i]);
  13219. }
  13220. };
  13221. CaptionScreen.prototype.isEmpty = function isEmpty() {
  13222. var empty = true;
  13223. for (var i = 0; i < NR_ROWS; i++) {
  13224. if (!this.rows[i].isEmpty()) {
  13225. empty = false;
  13226. break;
  13227. }
  13228. }
  13229. return empty;
  13230. };
  13231. CaptionScreen.prototype.backSpace = function backSpace() {
  13232. var row = this.rows[this.currRow];
  13233. row.backSpace();
  13234. };
  13235. CaptionScreen.prototype.clearToEndOfRow = function clearToEndOfRow() {
  13236. var row = this.rows[this.currRow];
  13237. row.clearToEndOfRow();
  13238. };
  13239. /**
  13240. * Insert a character (without styling) in the current row.
  13241. */
  13242. CaptionScreen.prototype.insertChar = function insertChar(char) {
  13243. var row = this.rows[this.currRow];
  13244. row.insertChar(char);
  13245. };
  13246. CaptionScreen.prototype.setPen = function setPen(styles) {
  13247. var row = this.rows[this.currRow];
  13248. row.setPenStyles(styles);
  13249. };
  13250. CaptionScreen.prototype.moveCursor = function moveCursor(relPos) {
  13251. var row = this.rows[this.currRow];
  13252. row.moveCursor(relPos);
  13253. };
  13254. CaptionScreen.prototype.setCursor = function setCursor(absPos) {
  13255. cea_608_parser_logger.log('INFO', 'setCursor: ' + absPos);
  13256. var row = this.rows[this.currRow];
  13257. row.setCursor(absPos);
  13258. };
  13259. CaptionScreen.prototype.setPAC = function setPAC(pacData) {
  13260. cea_608_parser_logger.log('INFO', 'pacData = ' + JSON.stringify(pacData));
  13261. var newRow = pacData.row - 1;
  13262. if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {
  13263. newRow = this.nrRollUpRows - 1;
  13264. }
  13265. //Make sure this only affects Roll-up Captions by checking this.nrRollUpRows
  13266. if (this.nrRollUpRows && this.currRow !== newRow) {
  13267. //clear all rows first
  13268. for (var i = 0; i < NR_ROWS; i++) {
  13269. this.rows[i].clear();
  13270. }
  13271. //Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location
  13272. //topRowIndex - the start of rows to copy (inclusive index)
  13273. var topRowIndex = this.currRow + 1 - this.nrRollUpRows;
  13274. //We only copy if the last position was already shown.
  13275. //We use the cueStartTime value to check this.
  13276. var lastOutputScreen = this.lastOutputScreen;
  13277. if (lastOutputScreen) {
  13278. var prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime;
  13279. if (prevLineTime && prevLineTime < cea_608_parser_logger.time) {
  13280. for (var _i = 0; _i < this.nrRollUpRows; _i++) {
  13281. this.rows[newRow - this.nrRollUpRows + _i + 1].copy(lastOutputScreen.rows[topRowIndex + _i]);
  13282. }
  13283. }
  13284. }
  13285. }
  13286. this.currRow = newRow;
  13287. var row = this.rows[this.currRow];
  13288. if (pacData.indent !== null) {
  13289. var indent = pacData.indent;
  13290. var prevPos = Math.max(indent - 1, 0);
  13291. row.setCursor(pacData.indent);
  13292. pacData.color = row.chars[prevPos].penState.foreground;
  13293. }
  13294. var styles = {
  13295. foreground: pacData.color,
  13296. underline: pacData.underline,
  13297. italics: pacData.italics,
  13298. background: 'black',
  13299. flash: false
  13300. };
  13301. this.setPen(styles);
  13302. };
  13303. /**
  13304. * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility).
  13305. */
  13306. CaptionScreen.prototype.setBkgData = function setBkgData(bkgData) {
  13307. cea_608_parser_logger.log('INFO', 'bkgData = ' + JSON.stringify(bkgData));
  13308. this.backSpace();
  13309. this.setPen(bkgData);
  13310. this.insertChar(0x20); //Space
  13311. };
  13312. CaptionScreen.prototype.setRollUpRows = function setRollUpRows(nrRows) {
  13313. this.nrRollUpRows = nrRows;
  13314. };
  13315. CaptionScreen.prototype.rollUp = function rollUp() {
  13316. if (this.nrRollUpRows === null) {
  13317. cea_608_parser_logger.log('DEBUG', 'roll_up but nrRollUpRows not set yet');
  13318. return; //Not properly setup
  13319. }
  13320. cea_608_parser_logger.log('TEXT', this.getDisplayText());
  13321. var topRowIndex = this.currRow + 1 - this.nrRollUpRows;
  13322. var topRow = this.rows.splice(topRowIndex, 1)[0];
  13323. topRow.clear();
  13324. this.rows.splice(this.currRow, 0, topRow);
  13325. cea_608_parser_logger.log('INFO', 'Rolling up');
  13326. //logger.log('TEXT', this.get_display_text())
  13327. };
  13328. /**
  13329. * Get all non-empty rows with as unicode text.
  13330. */
  13331. CaptionScreen.prototype.getDisplayText = function getDisplayText(asOneRow) {
  13332. asOneRow = asOneRow || false;
  13333. var displayText = [];
  13334. var text = '';
  13335. var rowNr = -1;
  13336. for (var i = 0; i < NR_ROWS; i++) {
  13337. var rowText = this.rows[i].getTextString();
  13338. if (rowText) {
  13339. rowNr = i + 1;
  13340. if (asOneRow) {
  13341. displayText.push('Row ' + rowNr + ': \'' + rowText + '\'');
  13342. } else {
  13343. displayText.push(rowText.trim());
  13344. }
  13345. }
  13346. }
  13347. if (displayText.length > 0) {
  13348. if (asOneRow) {
  13349. text = '[' + displayText.join(' | ') + ']';
  13350. } else {
  13351. text = displayText.join('\n');
  13352. }
  13353. }
  13354. return text;
  13355. };
  13356. CaptionScreen.prototype.getTextAndFormat = function getTextAndFormat() {
  13357. return this.rows;
  13358. };
  13359. return CaptionScreen;
  13360. }();
  13361. //var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT'];
  13362. var Cea608Channel = function () {
  13363. function Cea608Channel(channelNumber, outputFilter) {
  13364. cea_608_parser__classCallCheck(this, Cea608Channel);
  13365. this.chNr = channelNumber;
  13366. this.outputFilter = outputFilter;
  13367. this.mode = null;
  13368. this.verbose = 0;
  13369. this.displayedMemory = new CaptionScreen();
  13370. this.nonDisplayedMemory = new CaptionScreen();
  13371. this.lastOutputScreen = new CaptionScreen();
  13372. this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
  13373. this.writeScreen = this.displayedMemory;
  13374. this.mode = null;
  13375. this.cueStartTime = null; // Keeps track of where a cue started.
  13376. }
  13377. Cea608Channel.prototype.reset = function reset() {
  13378. this.mode = null;
  13379. this.displayedMemory.reset();
  13380. this.nonDisplayedMemory.reset();
  13381. this.lastOutputScreen.reset();
  13382. this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
  13383. this.writeScreen = this.displayedMemory;
  13384. this.mode = null;
  13385. this.cueStartTime = null;
  13386. this.lastCueEndTime = null;
  13387. };
  13388. Cea608Channel.prototype.getHandler = function getHandler() {
  13389. return this.outputFilter;
  13390. };
  13391. Cea608Channel.prototype.setHandler = function setHandler(newHandler) {
  13392. this.outputFilter = newHandler;
  13393. };
  13394. Cea608Channel.prototype.setPAC = function setPAC(pacData) {
  13395. this.writeScreen.setPAC(pacData);
  13396. };
  13397. Cea608Channel.prototype.setBkgData = function setBkgData(bkgData) {
  13398. this.writeScreen.setBkgData(bkgData);
  13399. };
  13400. Cea608Channel.prototype.setMode = function setMode(newMode) {
  13401. if (newMode === this.mode) {
  13402. return;
  13403. }
  13404. this.mode = newMode;
  13405. cea_608_parser_logger.log('INFO', 'MODE=' + newMode);
  13406. if (this.mode === 'MODE_POP-ON') {
  13407. this.writeScreen = this.nonDisplayedMemory;
  13408. } else {
  13409. this.writeScreen = this.displayedMemory;
  13410. this.writeScreen.reset();
  13411. }
  13412. if (this.mode !== 'MODE_ROLL-UP') {
  13413. this.displayedMemory.nrRollUpRows = null;
  13414. this.nonDisplayedMemory.nrRollUpRows = null;
  13415. }
  13416. this.mode = newMode;
  13417. };
  13418. Cea608Channel.prototype.insertChars = function insertChars(chars) {
  13419. for (var i = 0; i < chars.length; i++) {
  13420. this.writeScreen.insertChar(chars[i]);
  13421. }
  13422. var screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP';
  13423. cea_608_parser_logger.log('INFO', screen + ': ' + this.writeScreen.getDisplayText(true));
  13424. if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') {
  13425. cea_608_parser_logger.log('TEXT', 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true));
  13426. this.outputDataUpdate();
  13427. }
  13428. };
  13429. Cea608Channel.prototype.ccRCL = function ccRCL() {
  13430. // Resume Caption Loading (switch mode to Pop On)
  13431. cea_608_parser_logger.log('INFO', 'RCL - Resume Caption Loading');
  13432. this.setMode('MODE_POP-ON');
  13433. };
  13434. Cea608Channel.prototype.ccBS = function ccBS() {
  13435. // BackSpace
  13436. cea_608_parser_logger.log('INFO', 'BS - BackSpace');
  13437. if (this.mode === 'MODE_TEXT') {
  13438. return;
  13439. }
  13440. this.writeScreen.backSpace();
  13441. if (this.writeScreen === this.displayedMemory) {
  13442. this.outputDataUpdate();
  13443. }
  13444. };
  13445. Cea608Channel.prototype.ccAOF = function ccAOF() {
  13446. // Reserved (formerly Alarm Off)
  13447. return;
  13448. };
  13449. Cea608Channel.prototype.ccAON = function ccAON() {
  13450. // Reserved (formerly Alarm On)
  13451. return;
  13452. };
  13453. Cea608Channel.prototype.ccDER = function ccDER() {
  13454. // Delete to End of Row
  13455. cea_608_parser_logger.log('INFO', 'DER- Delete to End of Row');
  13456. this.writeScreen.clearToEndOfRow();
  13457. this.outputDataUpdate();
  13458. };
  13459. Cea608Channel.prototype.ccRU = function ccRU(nrRows) {
  13460. //Roll-Up Captions-2,3,or 4 Rows
  13461. cea_608_parser_logger.log('INFO', 'RU(' + nrRows + ') - Roll Up');
  13462. this.writeScreen = this.displayedMemory;
  13463. this.setMode('MODE_ROLL-UP');
  13464. this.writeScreen.setRollUpRows(nrRows);
  13465. };
  13466. Cea608Channel.prototype.ccFON = function ccFON() {
  13467. //Flash On
  13468. cea_608_parser_logger.log('INFO', 'FON - Flash On');
  13469. this.writeScreen.setPen({flash: true});
  13470. };
  13471. Cea608Channel.prototype.ccRDC = function ccRDC() {
  13472. // Resume Direct Captioning (switch mode to PaintOn)
  13473. cea_608_parser_logger.log('INFO', 'RDC - Resume Direct Captioning');
  13474. this.setMode('MODE_PAINT-ON');
  13475. };
  13476. Cea608Channel.prototype.ccTR = function ccTR() {
  13477. // Text Restart in text mode (not supported, however)
  13478. cea_608_parser_logger.log('INFO', 'TR');
  13479. this.setMode('MODE_TEXT');
  13480. };
  13481. Cea608Channel.prototype.ccRTD = function ccRTD() {
  13482. // Resume Text Display in Text mode (not supported, however)
  13483. cea_608_parser_logger.log('INFO', 'RTD');
  13484. this.setMode('MODE_TEXT');
  13485. };
  13486. Cea608Channel.prototype.ccEDM = function ccEDM() {
  13487. // Erase Displayed Memory
  13488. cea_608_parser_logger.log('INFO', 'EDM - Erase Displayed Memory');
  13489. this.displayedMemory.reset();
  13490. this.outputDataUpdate(true);
  13491. };
  13492. Cea608Channel.prototype.ccCR = function ccCR() {
  13493. // Carriage Return
  13494. cea_608_parser_logger.log('CR - Carriage Return');
  13495. this.writeScreen.rollUp();
  13496. this.outputDataUpdate(true);
  13497. };
  13498. Cea608Channel.prototype.ccENM = function ccENM() {
  13499. //Erase Non-Displayed Memory
  13500. cea_608_parser_logger.log('INFO', 'ENM - Erase Non-displayed Memory');
  13501. this.nonDisplayedMemory.reset();
  13502. };
  13503. Cea608Channel.prototype.ccEOC = function ccEOC() {
  13504. //End of Caption (Flip Memories)
  13505. cea_608_parser_logger.log('INFO', 'EOC - End Of Caption');
  13506. if (this.mode === 'MODE_POP-ON') {
  13507. var tmp = this.displayedMemory;
  13508. this.displayedMemory = this.nonDisplayedMemory;
  13509. this.nonDisplayedMemory = tmp;
  13510. this.writeScreen = this.nonDisplayedMemory;
  13511. cea_608_parser_logger.log('TEXT', 'DISP: ' + this.displayedMemory.getDisplayText());
  13512. }
  13513. this.outputDataUpdate(true);
  13514. };
  13515. Cea608Channel.prototype.ccTO = function ccTO(nrCols) {
  13516. // Tab Offset 1,2, or 3 columns
  13517. cea_608_parser_logger.log('INFO', 'TO(' + nrCols + ') - Tab Offset');
  13518. this.writeScreen.moveCursor(nrCols);
  13519. };
  13520. Cea608Channel.prototype.ccMIDROW = function ccMIDROW(secondByte) {
  13521. // Parse MIDROW command
  13522. var styles = {flash: false};
  13523. styles.underline = secondByte % 2 === 1;
  13524. styles.italics = secondByte >= 0x2e;
  13525. if (!styles.italics) {
  13526. var colorIndex = Math.floor(secondByte / 2) - 0x10;
  13527. var colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta'];
  13528. styles.foreground = colors[colorIndex];
  13529. } else {
  13530. styles.foreground = 'white';
  13531. }
  13532. cea_608_parser_logger.log('INFO', 'MIDROW: ' + JSON.stringify(styles));
  13533. this.writeScreen.setPen(styles);
  13534. };
  13535. Cea608Channel.prototype.outputDataUpdate = function outputDataUpdate() {
  13536. var dispatch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  13537. var t = cea_608_parser_logger.time;
  13538. if (t === null) {
  13539. return;
  13540. }
  13541. if (this.outputFilter) {
  13542. if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) {
  13543. // Start of a new cue
  13544. this.cueStartTime = t;
  13545. } else {
  13546. if (!this.displayedMemory.equals(this.lastOutputScreen)) {
  13547. if (this.outputFilter.newCue) {
  13548. this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen);
  13549. if (dispatch === true && this.outputFilter.dispatchCue) {
  13550. this.outputFilter.dispatchCue();
  13551. }
  13552. }
  13553. this.cueStartTime = this.displayedMemory.isEmpty() ? null : t;
  13554. }
  13555. }
  13556. this.lastOutputScreen.copy(this.displayedMemory);
  13557. }
  13558. };
  13559. Cea608Channel.prototype.cueSplitAtTime = function cueSplitAtTime(t) {
  13560. if (this.outputFilter) {
  13561. if (!this.displayedMemory.isEmpty()) {
  13562. if (this.outputFilter.newCue) {
  13563. this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory);
  13564. }
  13565. this.cueStartTime = t;
  13566. }
  13567. }
  13568. };
  13569. return Cea608Channel;
  13570. }();
  13571. var Cea608Parser = function () {
  13572. function Cea608Parser(field, out1, out2) {
  13573. cea_608_parser__classCallCheck(this, Cea608Parser);
  13574. this.field = field || 1;
  13575. this.outputs = [out1, out2];
  13576. this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)];
  13577. this.currChNr = -1; // Will be 1 or 2
  13578. this.lastCmdA = null; // First byte of last command
  13579. this.lastCmdB = null; // Second byte of last command
  13580. this.bufferedData = [];
  13581. this.startTime = null;
  13582. this.lastTime = null;
  13583. this.dataCounters = {'padding': 0, 'char': 0, 'cmd': 0, 'other': 0};
  13584. }
  13585. Cea608Parser.prototype.getHandler = function getHandler(index) {
  13586. return this.channels[index].getHandler();
  13587. };
  13588. Cea608Parser.prototype.setHandler = function setHandler(index, newHandler) {
  13589. this.channels[index].setHandler(newHandler);
  13590. };
  13591. /**
  13592. * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs.
  13593. */
  13594. Cea608Parser.prototype.addData = function addData(t, byteList) {
  13595. var cmdFound,
  13596. a,
  13597. b,
  13598. charsFound = false;
  13599. this.lastTime = t;
  13600. cea_608_parser_logger.setTime(t);
  13601. for (var i = 0; i < byteList.length; i += 2) {
  13602. a = byteList[i] & 0x7f;
  13603. b = byteList[i + 1] & 0x7f;
  13604. if (a === 0 && b === 0) {
  13605. this.dataCounters.padding += 2;
  13606. continue;
  13607. } else {
  13608. cea_608_parser_logger.log('DATA', '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')');
  13609. }
  13610. cmdFound = this.parseCmd(a, b);
  13611. if (!cmdFound) {
  13612. cmdFound = this.parseMidrow(a, b);
  13613. }
  13614. if (!cmdFound) {
  13615. cmdFound = this.parsePAC(a, b);
  13616. }
  13617. if (!cmdFound) {
  13618. cmdFound = this.parseBackgroundAttributes(a, b);
  13619. }
  13620. if (!cmdFound) {
  13621. charsFound = this.parseChars(a, b);
  13622. if (charsFound) {
  13623. if (this.currChNr && this.currChNr >= 0) {
  13624. var channel = this.channels[this.currChNr - 1];
  13625. channel.insertChars(charsFound);
  13626. } else {
  13627. cea_608_parser_logger.log('WARNING', 'No channel found yet. TEXT-MODE?');
  13628. }
  13629. }
  13630. }
  13631. if (cmdFound) {
  13632. this.dataCounters.cmd += 2;
  13633. } else if (charsFound) {
  13634. this.dataCounters.char += 2;
  13635. } else {
  13636. this.dataCounters.other += 2;
  13637. cea_608_parser_logger.log('WARNING', 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]]));
  13638. }
  13639. }
  13640. };
  13641. /**
  13642. * Parse Command.
  13643. * @returns {Boolean} Tells if a command was found
  13644. */
  13645. Cea608Parser.prototype.parseCmd = function parseCmd(a, b) {
  13646. var chNr = null;
  13647. var cond1 = (a === 0x14 || a === 0x1C) && 0x20 <= b && b <= 0x2F;
  13648. var cond2 = (a === 0x17 || a === 0x1F) && 0x21 <= b && b <= 0x23;
  13649. if (!(cond1 || cond2)) {
  13650. return false;
  13651. }
  13652. if (a === this.lastCmdA && b === this.lastCmdB) {
  13653. this.lastCmdA = null;
  13654. this.lastCmdB = null; // Repeated commands are dropped (once)
  13655. cea_608_parser_logger.log('DEBUG', 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped');
  13656. return true;
  13657. }
  13658. if (a === 0x14 || a === 0x17) {
  13659. chNr = 1;
  13660. } else {
  13661. chNr = 2; // (a === 0x1C || a=== 0x1f)
  13662. }
  13663. var channel = this.channels[chNr - 1];
  13664. if (a === 0x14 || a === 0x1C) {
  13665. if (b === 0x20) {
  13666. channel.ccRCL();
  13667. } else if (b === 0x21) {
  13668. channel.ccBS();
  13669. } else if (b === 0x22) {
  13670. channel.ccAOF();
  13671. } else if (b === 0x23) {
  13672. channel.ccAON();
  13673. } else if (b === 0x24) {
  13674. channel.ccDER();
  13675. } else if (b === 0x25) {
  13676. channel.ccRU(2);
  13677. } else if (b === 0x26) {
  13678. channel.ccRU(3);
  13679. } else if (b === 0x27) {
  13680. channel.ccRU(4);
  13681. } else if (b === 0x28) {
  13682. channel.ccFON();
  13683. } else if (b === 0x29) {
  13684. channel.ccRDC();
  13685. } else if (b === 0x2A) {
  13686. channel.ccTR();
  13687. } else if (b === 0x2B) {
  13688. channel.ccRTD();
  13689. } else if (b === 0x2C) {
  13690. channel.ccEDM();
  13691. } else if (b === 0x2D) {
  13692. channel.ccCR();
  13693. } else if (b === 0x2E) {
  13694. channel.ccENM();
  13695. } else if (b === 0x2F) {
  13696. channel.ccEOC();
  13697. }
  13698. } else {
  13699. //a == 0x17 || a == 0x1F
  13700. channel.ccTO(b - 0x20);
  13701. }
  13702. this.lastCmdA = a;
  13703. this.lastCmdB = b;
  13704. this.currChNr = chNr;
  13705. return true;
  13706. };
  13707. /**
  13708. * Parse midrow styling command
  13709. * @returns {Boolean}
  13710. */
  13711. Cea608Parser.prototype.parseMidrow = function parseMidrow(a, b) {
  13712. var chNr = null;
  13713. if ((a === 0x11 || a === 0x19) && 0x20 <= b && b <= 0x2f) {
  13714. if (a === 0x11) {
  13715. chNr = 1;
  13716. } else {
  13717. chNr = 2;
  13718. }
  13719. if (chNr !== this.currChNr) {
  13720. cea_608_parser_logger.log('ERROR', 'Mismatch channel in midrow parsing');
  13721. return false;
  13722. }
  13723. var channel = this.channels[chNr - 1];
  13724. channel.ccMIDROW(b);
  13725. cea_608_parser_logger.log('DEBUG', 'MIDROW (' + numArrayToHexArray([a, b]) + ')');
  13726. return true;
  13727. }
  13728. return false;
  13729. };
  13730. /**
  13731. * Parse Preable Access Codes (Table 53).
  13732. * @returns {Boolean} Tells if PAC found
  13733. */
  13734. Cea608Parser.prototype.parsePAC = function parsePAC(a, b) {
  13735. var chNr = null;
  13736. var row = null;
  13737. var case1 = (0x11 <= a && a <= 0x17 || 0x19 <= a && a <= 0x1F) && 0x40 <= b && b <= 0x7F;
  13738. var case2 = (a === 0x10 || a === 0x18) && 0x40 <= b && b <= 0x5F;
  13739. if (!(case1 || case2)) {
  13740. return false;
  13741. }
  13742. if (a === this.lastCmdA && b === this.lastCmdB) {
  13743. this.lastCmdA = null;
  13744. this.lastCmdB = null;
  13745. return true; // Repeated commands are dropped (once)
  13746. }
  13747. chNr = a <= 0x17 ? 1 : 2;
  13748. if (0x40 <= b && b <= 0x5F) {
  13749. row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a];
  13750. } else {
  13751. // 0x60 <= b <= 0x7F
  13752. row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a];
  13753. }
  13754. var pacData = this.interpretPAC(row, b);
  13755. var channel = this.channels[chNr - 1];
  13756. channel.setPAC(pacData);
  13757. this.lastCmdA = a;
  13758. this.lastCmdB = b;
  13759. this.currChNr = chNr;
  13760. return true;
  13761. };
  13762. /**
  13763. * Interpret the second byte of the pac, and return the information.
  13764. * @returns {Object} pacData with style parameters.
  13765. */
  13766. Cea608Parser.prototype.interpretPAC = function interpretPAC(row, byte) {
  13767. var pacIndex = byte;
  13768. var pacData = {color: null, italics: false, indent: null, underline: false, row: row};
  13769. if (byte > 0x5F) {
  13770. pacIndex = byte - 0x60;
  13771. } else {
  13772. pacIndex = byte - 0x40;
  13773. }
  13774. pacData.underline = (pacIndex & 1) === 1;
  13775. if (pacIndex <= 0xd) {
  13776. pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)];
  13777. } else if (pacIndex <= 0xf) {
  13778. pacData.italics = true;
  13779. pacData.color = 'white';
  13780. } else {
  13781. pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4;
  13782. }
  13783. return pacData; // Note that row has zero offset. The spec uses 1.
  13784. };
  13785. /**
  13786. * Parse characters.
  13787. * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise.
  13788. */
  13789. Cea608Parser.prototype.parseChars = function parseChars(a, b) {
  13790. var channelNr = null,
  13791. charCodes = null,
  13792. charCode1 = null;
  13793. if (a >= 0x19) {
  13794. channelNr = 2;
  13795. charCode1 = a - 8;
  13796. } else {
  13797. channelNr = 1;
  13798. charCode1 = a;
  13799. }
  13800. if (0x11 <= charCode1 && charCode1 <= 0x13) {
  13801. // Special character
  13802. var oneCode = b;
  13803. if (charCode1 === 0x11) {
  13804. oneCode = b + 0x50;
  13805. } else if (charCode1 === 0x12) {
  13806. oneCode = b + 0x70;
  13807. } else {
  13808. oneCode = b + 0x90;
  13809. }
  13810. cea_608_parser_logger.log('INFO', 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr);
  13811. charCodes = [oneCode];
  13812. } else if (0x20 <= a && a <= 0x7f) {
  13813. charCodes = b === 0 ? [a] : [a, b];
  13814. }
  13815. if (charCodes) {
  13816. var hexCodes = numArrayToHexArray(charCodes);
  13817. cea_608_parser_logger.log('DEBUG', 'Char codes = ' + hexCodes.join(','));
  13818. this.lastCmdA = null;
  13819. this.lastCmdB = null;
  13820. }
  13821. return charCodes;
  13822. };
  13823. /**
  13824. * Parse extended background attributes as well as new foreground color black.
  13825. * @returns{Boolean} Tells if background attributes are found
  13826. */
  13827. Cea608Parser.prototype.parseBackgroundAttributes = function parseBackgroundAttributes(a, b) {
  13828. var bkgData, index, chNr, channel;
  13829. var case1 = (a === 0x10 || a === 0x18) && 0x20 <= b && b <= 0x2f;
  13830. var case2 = (a === 0x17 || a === 0x1f) && 0x2d <= b && b <= 0x2f;
  13831. if (!(case1 || case2)) {
  13832. return false;
  13833. }
  13834. bkgData = {};
  13835. if (a === 0x10 || a === 0x18) {
  13836. index = Math.floor((b - 0x20) / 2);
  13837. bkgData.background = backgroundColors[index];
  13838. if (b % 2 === 1) {
  13839. bkgData.background = bkgData.background + '_semi';
  13840. }
  13841. } else if (b === 0x2d) {
  13842. bkgData.background = 'transparent';
  13843. } else {
  13844. bkgData.foreground = 'black';
  13845. if (b === 0x2f) {
  13846. bkgData.underline = true;
  13847. }
  13848. }
  13849. chNr = a < 0x18 ? 1 : 2;
  13850. channel = this.channels[chNr - 1];
  13851. channel.setBkgData(bkgData);
  13852. this.lastCmdA = null;
  13853. this.lastCmdB = null;
  13854. return true;
  13855. };
  13856. /**
  13857. * Reset state of parser and its channels.
  13858. */
  13859. Cea608Parser.prototype.reset = function reset() {
  13860. for (var i = 0; i < this.channels.length; i++) {
  13861. if (this.channels[i]) {
  13862. this.channels[i].reset();
  13863. }
  13864. }
  13865. this.lastCmdA = null;
  13866. this.lastCmdB = null;
  13867. };
  13868. /**
  13869. * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty.
  13870. */
  13871. Cea608Parser.prototype.cueSplitAtTime = function cueSplitAtTime(t) {
  13872. for (var i = 0; i < this.channels.length; i++) {
  13873. if (this.channels[i]) {
  13874. this.channels[i].cueSplitAtTime(t);
  13875. }
  13876. }
  13877. };
  13878. return Cea608Parser;
  13879. }();
  13880. /* harmony default export */
  13881. var cea_608_parser = (Cea608Parser);
  13882. // CONCATENATED MODULE: ./src/utils/output-filter.js
  13883. function output_filter__classCallCheck(instance, Constructor) {
  13884. if (!(instance instanceof Constructor)) {
  13885. throw new TypeError("Cannot call a class as a function");
  13886. }
  13887. }
  13888. var OutputFilter = function () {
  13889. function OutputFilter(timelineController, track) {
  13890. output_filter__classCallCheck(this, OutputFilter);
  13891. this.timelineController = timelineController;
  13892. this.track = track;
  13893. this.startTime = null;
  13894. this.endTime = null;
  13895. this.screen = null;
  13896. }
  13897. OutputFilter.prototype.dispatchCue = function dispatchCue() {
  13898. if (this.startTime === null) {
  13899. return;
  13900. }
  13901. this.timelineController.addCues('textTrack' + this.track, this.startTime, this.endTime, this.screen);
  13902. this.startTime = null;
  13903. };
  13904. OutputFilter.prototype.newCue = function newCue(startTime, endTime, screen) {
  13905. if (this.startTime === null || this.startTime > startTime) {
  13906. this.startTime = startTime;
  13907. }
  13908. this.endTime = endTime;
  13909. this.screen = screen;
  13910. this.timelineController.createCaptionsTrack(this.track);
  13911. };
  13912. return OutputFilter;
  13913. }();
  13914. /* harmony default export */
  13915. var output_filter = (OutputFilter);
  13916. // CONCATENATED MODULE: ./src/utils/webvtt-parser.js
  13917. // String.prototype.startsWith is not supported in IE11
  13918. var startsWith = function startsWith(inputString, searchString, position) {
  13919. return inputString.substr(position || 0, searchString.length) === searchString;
  13920. };
  13921. var cueString2millis = function cueString2millis(timeString) {
  13922. var ts = parseInt(timeString.substr(-3));
  13923. var secs = parseInt(timeString.substr(-6, 2));
  13924. var mins = parseInt(timeString.substr(-9, 2));
  13925. var hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0;
  13926. if (isNaN(ts) || isNaN(secs) || isNaN(mins) || isNaN(hours)) {
  13927. return -1;
  13928. }
  13929. ts += 1000 * secs;
  13930. ts += 60 * 1000 * mins;
  13931. ts += 60 * 60 * 1000 * hours;
  13932. return ts;
  13933. };
  13934. // From https://github.com/darkskyapp/string-hash
  13935. var hash = function hash(text) {
  13936. var hash = 5381;
  13937. var i = text.length;
  13938. while (i) {
  13939. hash = hash * 33 ^ text.charCodeAt(--i);
  13940. }
  13941. return (hash >>> 0).toString();
  13942. };
  13943. var calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) {
  13944. var currCC = vttCCs[cc];
  13945. var prevCC = vttCCs[currCC.prevCC];
  13946. // This is the first discontinuity or cues have been processed since the last discontinuity
  13947. // Offset = current discontinuity time
  13948. if (!prevCC || !prevCC.new && currCC.new) {
  13949. vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start;
  13950. currCC.new = false;
  13951. return;
  13952. }
  13953. // There have been discontinuities since cues were last parsed.
  13954. // Offset = time elapsed
  13955. while (prevCC && prevCC.new) {
  13956. vttCCs.ccOffset += currCC.start - prevCC.start;
  13957. currCC.new = false;
  13958. currCC = prevCC;
  13959. prevCC = vttCCs[currCC.prevCC];
  13960. }
  13961. vttCCs.presentationOffset = presentationTime;
  13962. };
  13963. var WebVTTParser = {
  13964. parse: function parse(vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) {
  13965. // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character.
  13966. var re = /\r\n|\n\r|\n|\r/g;
  13967. // Uint8Array.prototype.reduce is not implemented in IE11
  13968. var vttLines = Object(id3["b" /* utf8ArrayToStr */])(new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n');
  13969. var cueTime = '00:00.000';
  13970. var mpegTs = 0;
  13971. var localTime = 0;
  13972. var presentationTime = 0;
  13973. var cues = [];
  13974. var parsingError = void 0;
  13975. var inHeader = true;
  13976. // let VTTCue = VTTCue || window.TextTrackCue;
  13977. // Create parser object using VTTCue with TextTrackCue fallback on certain browsers.
  13978. var parser = new vttparser();
  13979. parser.oncue = function (cue) {
  13980. // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline.
  13981. var currCC = vttCCs[cc];
  13982. var cueOffset = vttCCs.ccOffset;
  13983. // Update offsets for new discontinuities
  13984. if (currCC && currCC.new) {
  13985. if (localTime !== undefined) {
  13986. // When local time is provided, offset = discontinuity start time - local time
  13987. cueOffset = vttCCs.ccOffset = currCC.start;
  13988. } else {
  13989. calculateOffset(vttCCs, cc, presentationTime);
  13990. }
  13991. }
  13992. if (presentationTime) {
  13993. // If we have MPEGTS, offset = presentation time + discontinuity offset
  13994. cueOffset = presentationTime + vttCCs.ccOffset - vttCCs.presentationOffset;
  13995. }
  13996. cue.startTime += cueOffset - localTime;
  13997. cue.endTime += cueOffset - localTime;
  13998. // Create a unique hash id for a cue based on start/end times and text.
  13999. // This helps timeline-controller to avoid showing repeated captions.
  14000. cue.id = hash(cue.startTime.toString()) + hash(cue.endTime.toString()) + hash(cue.text);
  14001. // Fix encoding of special characters. TODO: Test with all sorts of weird characters.
  14002. cue.text = decodeURIComponent(encodeURIComponent(cue.text));
  14003. if (cue.endTime > 0) {
  14004. cues.push(cue);
  14005. }
  14006. };
  14007. parser.onparsingerror = function (e) {
  14008. parsingError = e;
  14009. };
  14010. parser.onflush = function () {
  14011. if (parsingError && errorCallBack) {
  14012. errorCallBack(parsingError);
  14013. return;
  14014. }
  14015. callBack(cues);
  14016. };
  14017. // Go through contents line by line.
  14018. vttLines.forEach(function (line) {
  14019. if (inHeader) {
  14020. // Look for X-TIMESTAMP-MAP in header.
  14021. if (startsWith(line, 'X-TIMESTAMP-MAP=')) {
  14022. // Once found, no more are allowed anyway, so stop searching.
  14023. inHeader = false;
  14024. // Extract LOCAL and MPEGTS.
  14025. line.substr(16).split(',').forEach(function (timestamp) {
  14026. if (startsWith(timestamp, 'LOCAL:')) {
  14027. cueTime = timestamp.substr(6);
  14028. } else if (startsWith(timestamp, 'MPEGTS:')) {
  14029. mpegTs = parseInt(timestamp.substr(7));
  14030. }
  14031. });
  14032. try {
  14033. // Calculate subtitle offset in milliseconds.
  14034. // If sync PTS is less than zero, we have a 33-bit wraparound, which is fixed by adding 2^33 = 8589934592.
  14035. syncPTS = syncPTS < 0 ? syncPTS + 8589934592 : syncPTS;
  14036. // Adjust MPEGTS by sync PTS.
  14037. mpegTs -= syncPTS;
  14038. // Convert cue time to seconds
  14039. localTime = cueString2millis(cueTime) / 1000;
  14040. // Convert MPEGTS to seconds from 90kHz.
  14041. presentationTime = mpegTs / 90000;
  14042. if (localTime === -1) {
  14043. parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
  14044. }
  14045. } catch (e) {
  14046. parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
  14047. }
  14048. // Return without parsing X-TIMESTAMP-MAP line.
  14049. return;
  14050. } else if (line === '') {
  14051. inHeader = false;
  14052. }
  14053. }
  14054. // Parse line by default.
  14055. parser.parse(line + '\n');
  14056. });
  14057. parser.flush();
  14058. }
  14059. };
  14060. /* harmony default export */
  14061. var webvtt_parser = (WebVTTParser);
  14062. // CONCATENATED MODULE: ./src/controller/timeline-controller.js
  14063. function timeline_controller__classCallCheck(instance, Constructor) {
  14064. if (!(instance instanceof Constructor)) {
  14065. throw new TypeError("Cannot call a class as a function");
  14066. }
  14067. }
  14068. function timeline_controller__possibleConstructorReturn(self, call) {
  14069. if (!self) {
  14070. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  14071. }
  14072. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  14073. }
  14074. function timeline_controller__inherits(subClass, superClass) {
  14075. if (typeof superClass !== "function" && superClass !== null) {
  14076. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  14077. }
  14078. subClass.prototype = Object.create(superClass && superClass.prototype, {
  14079. constructor: {
  14080. value: subClass,
  14081. enumerable: false,
  14082. writable: true,
  14083. configurable: true
  14084. }
  14085. });
  14086. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  14087. }
  14088. /*
  14089. * Timeline Controller
  14090. */
  14091. function clearCurrentCues(track) {
  14092. if (track && track.cues) {
  14093. while (track.cues.length > 0) {
  14094. track.removeCue(track.cues[0]);
  14095. }
  14096. }
  14097. }
  14098. function reuseVttTextTrack(inUseTrack, manifestTrack) {
  14099. return inUseTrack && inUseTrack.label === manifestTrack.name && !(inUseTrack.textTrack1 || inUseTrack.textTrack2);
  14100. }
  14101. function intersection(x1, x2, y1, y2) {
  14102. return Math.min(x2, y2) - Math.max(x1, y1);
  14103. }
  14104. var timeline_controller_TimelineController = function (_EventHandler) {
  14105. timeline_controller__inherits(TimelineController, _EventHandler);
  14106. function TimelineController(hls) {
  14107. timeline_controller__classCallCheck(this, TimelineController);
  14108. var _this = timeline_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHING, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].FRAG_PARSING_USERDATA, events["a" /* default */].FRAG_DECRYPTED, events["a" /* default */].MANIFEST_LOADING, events["a" /* default */].MANIFEST_LOADED, events["a" /* default */].FRAG_LOADED, events["a" /* default */].LEVEL_SWITCHING, events["a" /* default */].INIT_PTS_FOUND));
  14109. _this.hls = hls;
  14110. _this.config = hls.config;
  14111. _this.enabled = true;
  14112. _this.Cues = hls.config.cueHandler;
  14113. _this.textTracks = [];
  14114. _this.tracks = [];
  14115. _this.unparsedVttFrags = [];
  14116. _this.initPTS = undefined;
  14117. _this.cueRanges = [];
  14118. if (_this.config.enableCEA708Captions) {
  14119. var channel1 = new output_filter(_this, 1);
  14120. var channel2 = new output_filter(_this, 2);
  14121. _this.cea608Parser = new cea_608_parser(0, channel1, channel2);
  14122. }
  14123. return _this;
  14124. }
  14125. TimelineController.prototype.addCues = function addCues(channel, startTime, endTime, screen) {
  14126. // skip cues which overlap more than 50% with previously parsed time ranges
  14127. var ranges = this.cueRanges;
  14128. var merged = false;
  14129. for (var i = ranges.length; i--;) {
  14130. var cueRange = ranges[i];
  14131. var overlap = intersection(cueRange[0], cueRange[1], startTime, endTime);
  14132. if (overlap >= 0) {
  14133. cueRange[0] = Math.min(cueRange[0], startTime);
  14134. cueRange[1] = Math.max(cueRange[1], endTime);
  14135. merged = true;
  14136. if (overlap / (endTime - startTime) > 0.5) {
  14137. return;
  14138. }
  14139. }
  14140. }
  14141. if (!merged) {
  14142. ranges.push([startTime, endTime]);
  14143. }
  14144. this.Cues.newCue(this[channel], startTime, endTime, screen);
  14145. };
  14146. // Triggered when an initial PTS is found; used for synchronisation of WebVTT.
  14147. TimelineController.prototype.onInitPtsFound = function onInitPtsFound(data) {
  14148. var _this2 = this;
  14149. if (typeof this.initPTS === 'undefined') {
  14150. this.initPTS = data.initPTS;
  14151. }
  14152. // Due to asynchrony, initial PTS may arrive later than the first VTT fragments are loaded.
  14153. // Parse any unparsed fragments upon receiving the initial PTS.
  14154. if (this.unparsedVttFrags.length) {
  14155. this.unparsedVttFrags.forEach(function (frag) {
  14156. _this2.onFragLoaded(frag);
  14157. });
  14158. this.unparsedVttFrags = [];
  14159. }
  14160. };
  14161. TimelineController.prototype.getExistingTrack = function getExistingTrack(channelNumber) {
  14162. var media = this.media;
  14163. if (media) {
  14164. for (var i = 0; i < media.textTracks.length; i++) {
  14165. var textTrack = media.textTracks[i];
  14166. var propName = 'textTrack' + channelNumber;
  14167. if (textTrack[propName] === true) {
  14168. return textTrack;
  14169. }
  14170. }
  14171. }
  14172. return null;
  14173. };
  14174. TimelineController.prototype.sendAddTrackEvent = function sendAddTrackEvent(track, media) {
  14175. var e = null;
  14176. try {
  14177. e = new window.Event('addtrack');
  14178. } catch (err) {
  14179. //for IE11
  14180. e = document.createEvent('Event');
  14181. e.initEvent('addtrack', false, false);
  14182. }
  14183. e.track = track;
  14184. media.dispatchEvent(e);
  14185. };
  14186. TimelineController.prototype.createCaptionsTrack = function createCaptionsTrack(track) {
  14187. var trackVar = 'textTrack' + track;
  14188. if (!this[trackVar]) {
  14189. //Enable reuse of existing text track.
  14190. var existingTrack = this.getExistingTrack(track);
  14191. if (!existingTrack) {
  14192. var textTrack = this.createTextTrack('captions', this.config['captionsTextTrack' + track + 'Label'], this.config.captionsTextTrack1LanguageCode);
  14193. if (textTrack) {
  14194. textTrack[trackVar] = true;
  14195. this[trackVar] = textTrack;
  14196. }
  14197. } else {
  14198. this[trackVar] = existingTrack;
  14199. clearCurrentCues(this[trackVar]);
  14200. this.sendAddTrackEvent(this[trackVar], this.media);
  14201. }
  14202. }
  14203. };
  14204. TimelineController.prototype.createTextTrack = function createTextTrack(kind, label, lang) {
  14205. var media = this.media;
  14206. if (media) {
  14207. return media.addTextTrack(kind, label, lang);
  14208. }
  14209. };
  14210. TimelineController.prototype.destroy = function destroy() {
  14211. event_handler.prototype.destroy.call(this);
  14212. };
  14213. TimelineController.prototype.onMediaAttaching = function onMediaAttaching(data) {
  14214. this.media = data.media;
  14215. this._cleanTracks();
  14216. };
  14217. TimelineController.prototype.onMediaDetaching = function onMediaDetaching() {
  14218. clearCurrentCues(this.textTrack1);
  14219. clearCurrentCues(this.textTrack2);
  14220. };
  14221. TimelineController.prototype.onManifestLoading = function onManifestLoading() {
  14222. this.lastSn = -1; // Detect discontiguity in fragment parsing
  14223. this.prevCC = -1;
  14224. this.vttCCs = {ccOffset: 0, presentationOffset: 0}; // Detect discontinuity in subtitle manifests
  14225. this._cleanTracks();
  14226. };
  14227. TimelineController.prototype._cleanTracks = function _cleanTracks() {
  14228. // clear outdated subtitles
  14229. var media = this.media;
  14230. if (media) {
  14231. var textTracks = media.textTracks;
  14232. if (textTracks) {
  14233. for (var i = 0; i < textTracks.length; i++) {
  14234. clearCurrentCues(textTracks[i]);
  14235. }
  14236. }
  14237. }
  14238. };
  14239. TimelineController.prototype.onManifestLoaded = function onManifestLoaded(data) {
  14240. var _this3 = this;
  14241. this.textTracks = [];
  14242. this.unparsedVttFrags = this.unparsedVttFrags || [];
  14243. this.initPTS = undefined;
  14244. this.cueRanges = [];
  14245. if (this.config.enableWebVTT) {
  14246. this.tracks = data.subtitles || [];
  14247. var inUseTracks = this.media ? this.media.textTracks : [];
  14248. this.tracks.forEach(function (track, index) {
  14249. var textTrack = void 0;
  14250. if (index < inUseTracks.length) {
  14251. var inUseTrack = inUseTracks[index];
  14252. // Reuse tracks with the same label, but do not reuse 608/708 tracks
  14253. if (reuseVttTextTrack(inUseTrack, track)) {
  14254. textTrack = inUseTrack;
  14255. }
  14256. }
  14257. if (!textTrack) {
  14258. textTrack = _this3.createTextTrack('subtitles', track.name, track.lang);
  14259. }
  14260. textTrack.mode = track.default ? 'showing' : 'hidden';
  14261. _this3.textTracks.push(textTrack);
  14262. });
  14263. }
  14264. };
  14265. TimelineController.prototype.onLevelSwitching = function onLevelSwitching() {
  14266. this.enabled = this.hls.currentLevel.closedCaptions !== 'NONE';
  14267. };
  14268. TimelineController.prototype.onFragLoaded = function onFragLoaded(data) {
  14269. var frag = data.frag,
  14270. payload = data.payload;
  14271. if (frag.type === 'main') {
  14272. var sn = frag.sn;
  14273. // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack
  14274. if (sn !== this.lastSn + 1) {
  14275. var cea608Parser = this.cea608Parser;
  14276. if (cea608Parser) {
  14277. cea608Parser.reset();
  14278. }
  14279. }
  14280. this.lastSn = sn;
  14281. }
  14282. // If fragment is subtitle type, parse as WebVTT.
  14283. else if (frag.type === 'subtitle') {
  14284. if (payload.byteLength) {
  14285. // We need an initial synchronisation PTS. Store fragments as long as none has arrived.
  14286. if (typeof this.initPTS === 'undefined') {
  14287. this.unparsedVttFrags.push(data);
  14288. return;
  14289. }
  14290. var decryptData = frag.decryptdata;
  14291. // If the subtitles are not encrypted, parse VTTs now. Otherwise, we need to wait.
  14292. if (decryptData == null || decryptData.key == null || decryptData.method !== 'AES-128') {
  14293. this._parseVTTs(frag, payload);
  14294. }
  14295. } else {
  14296. // In case there is no payload, finish unsuccessfully.
  14297. this.hls.trigger(events["a" /* default */].SUBTITLE_FRAG_PROCESSED, {
  14298. success: false,
  14299. frag: frag
  14300. });
  14301. }
  14302. }
  14303. };
  14304. TimelineController.prototype._parseVTTs = function _parseVTTs(frag, payload) {
  14305. var vttCCs = this.vttCCs;
  14306. if (!vttCCs[frag.cc]) {
  14307. vttCCs[frag.cc] = {start: frag.start, prevCC: this.prevCC, new: true};
  14308. this.prevCC = frag.cc;
  14309. }
  14310. var textTracks = this.textTracks,
  14311. hls = this.hls;
  14312. // Parse the WebVTT file contents.
  14313. webvtt_parser.parse(payload, this.initPTS, vttCCs, frag.cc, function (cues) {
  14314. var currentTrack = textTracks[frag.trackId];
  14315. // Add cues and trigger event with success true.
  14316. cues.forEach(function (cue) {
  14317. // Sometimes there are cue overlaps on segmented vtts so the same
  14318. // cue can appear more than once in different vtt files.
  14319. // This avoid showing duplicated cues with same timecode and text.
  14320. if (!currentTrack.cues.getCueById(cue.id)) {
  14321. try {
  14322. currentTrack.addCue(cue);
  14323. } catch (err) {
  14324. var textTrackCue = new window.TextTrackCue(cue.startTime, cue.endTime, cue.text);
  14325. textTrackCue.id = cue.id;
  14326. currentTrack.addCue(textTrackCue);
  14327. }
  14328. }
  14329. });
  14330. hls.trigger(events["a" /* default */].SUBTITLE_FRAG_PROCESSED, {success: true, frag: frag});
  14331. }, function (e) {
  14332. // Something went wrong while parsing. Trigger event with success false.
  14333. logger["b" /* logger */].log('Failed to parse VTT cue: ' + e);
  14334. hls.trigger(events["a" /* default */].SUBTITLE_FRAG_PROCESSED, {
  14335. success: false,
  14336. frag: frag
  14337. });
  14338. });
  14339. };
  14340. TimelineController.prototype.onFragDecrypted = function onFragDecrypted(data) {
  14341. var decryptedData = data.payload,
  14342. frag = data.frag;
  14343. if (frag.type === 'subtitle') {
  14344. if (typeof this.initPTS === 'undefined') {
  14345. this.unparsedVttFrags.push(data);
  14346. return;
  14347. }
  14348. this._parseVTTs(frag, decryptedData);
  14349. }
  14350. };
  14351. TimelineController.prototype.onFragParsingUserdata = function onFragParsingUserdata(data) {
  14352. // push all of the CEA-708 messages into the interpreter
  14353. // immediately. It will create the proper timestamps based on our PTS value
  14354. if (this.enabled && this.config.enableCEA708Captions) {
  14355. for (var i = 0; i < data.samples.length; i++) {
  14356. var ccdatas = this.extractCea608Data(data.samples[i].bytes);
  14357. this.cea608Parser.addData(data.samples[i].pts, ccdatas);
  14358. }
  14359. }
  14360. };
  14361. TimelineController.prototype.extractCea608Data = function extractCea608Data(byteArray) {
  14362. var count = byteArray[0] & 31;
  14363. var position = 2;
  14364. var tmpByte, ccbyte1, ccbyte2, ccValid, ccType;
  14365. var actualCCBytes = [];
  14366. for (var j = 0; j < count; j++) {
  14367. tmpByte = byteArray[position++];
  14368. ccbyte1 = 0x7F & byteArray[position++];
  14369. ccbyte2 = 0x7F & byteArray[position++];
  14370. ccValid = (4 & tmpByte) !== 0;
  14371. ccType = 3 & tmpByte;
  14372. if (ccbyte1 === 0 && ccbyte2 === 0) {
  14373. continue;
  14374. }
  14375. if (ccValid) {
  14376. if (ccType === 0) // || ccType === 1
  14377. {
  14378. actualCCBytes.push(ccbyte1);
  14379. actualCCBytes.push(ccbyte2);
  14380. }
  14381. }
  14382. }
  14383. return actualCCBytes;
  14384. };
  14385. return TimelineController;
  14386. }(event_handler);
  14387. /* harmony default export */
  14388. var timeline_controller = (timeline_controller_TimelineController);
  14389. // CONCATENATED MODULE: ./src/controller/subtitle-track-controller.js
  14390. var subtitle_track_controller__createClass = function () {
  14391. function defineProperties(target, props) {
  14392. for (var i = 0; i < props.length; i++) {
  14393. var descriptor = props[i];
  14394. descriptor.enumerable = descriptor.enumerable || false;
  14395. descriptor.configurable = true;
  14396. if ("value" in descriptor) descriptor.writable = true;
  14397. Object.defineProperty(target, descriptor.key, descriptor);
  14398. }
  14399. }
  14400. return function (Constructor, protoProps, staticProps) {
  14401. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  14402. if (staticProps) defineProperties(Constructor, staticProps);
  14403. return Constructor;
  14404. };
  14405. }();
  14406. function subtitle_track_controller__classCallCheck(instance, Constructor) {
  14407. if (!(instance instanceof Constructor)) {
  14408. throw new TypeError("Cannot call a class as a function");
  14409. }
  14410. }
  14411. function subtitle_track_controller__possibleConstructorReturn(self, call) {
  14412. if (!self) {
  14413. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  14414. }
  14415. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  14416. }
  14417. function subtitle_track_controller__inherits(subClass, superClass) {
  14418. if (typeof superClass !== "function" && superClass !== null) {
  14419. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  14420. }
  14421. subClass.prototype = Object.create(superClass && superClass.prototype, {
  14422. constructor: {
  14423. value: subClass,
  14424. enumerable: false,
  14425. writable: true,
  14426. configurable: true
  14427. }
  14428. });
  14429. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  14430. }
  14431. /*
  14432. * subtitle track controller
  14433. */
  14434. function filterSubtitleTracks(textTrackList) {
  14435. var tracks = [];
  14436. for (var i = 0; i < textTrackList.length; i++) {
  14437. if (textTrackList[i].kind === 'subtitles') {
  14438. tracks.push(textTrackList[i]);
  14439. }
  14440. }
  14441. return tracks;
  14442. }
  14443. var subtitle_track_controller_SubtitleTrackController = function (_EventHandler) {
  14444. subtitle_track_controller__inherits(SubtitleTrackController, _EventHandler);
  14445. function SubtitleTrackController(hls) {
  14446. subtitle_track_controller__classCallCheck(this, SubtitleTrackController);
  14447. var _this = subtitle_track_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].MEDIA_DETACHING, events["a" /* default */].MANIFEST_LOADING, events["a" /* default */].MANIFEST_LOADED, events["a" /* default */].SUBTITLE_TRACK_LOADED));
  14448. _this.tracks = [];
  14449. _this.trackId = -1;
  14450. _this.media = undefined;
  14451. _this.subtitleDisplay = false;
  14452. return _this;
  14453. }
  14454. SubtitleTrackController.prototype._onTextTracksChanged = function _onTextTracksChanged() {
  14455. // Media is undefined when switching streams via loadSource()
  14456. if (!this.media) {
  14457. return;
  14458. }
  14459. var trackId = -1;
  14460. var tracks = filterSubtitleTracks(this.media.textTracks);
  14461. for (var id = 0; id < tracks.length; id++) {
  14462. if (tracks[id].mode === 'showing') {
  14463. trackId = id;
  14464. }
  14465. }
  14466. // Setting current subtitleTrack will invoke code.
  14467. this.subtitleTrack = trackId;
  14468. };
  14469. SubtitleTrackController.prototype.destroy = function destroy() {
  14470. event_handler.prototype.destroy.call(this);
  14471. };
  14472. // Listen for subtitle track change, then extract the current track ID.
  14473. SubtitleTrackController.prototype.onMediaAttached = function onMediaAttached(data) {
  14474. var _this2 = this;
  14475. this.media = data.media;
  14476. if (!this.media) {
  14477. return;
  14478. }
  14479. if (this.queuedDefaultTrack !== undefined) {
  14480. this.subtitleTrack = this.queuedDefaultTrack;
  14481. delete this.queuedDefaultTrack;
  14482. }
  14483. this.trackChangeListener = this._onTextTracksChanged.bind(this);
  14484. this.useTextTrackPolling = !(this.media.textTracks && 'onchange' in this.media.textTracks);
  14485. if (this.useTextTrackPolling) {
  14486. this.subtitlePollingInterval = setInterval(function () {
  14487. _this2.trackChangeListener();
  14488. }, 500);
  14489. } else {
  14490. this.media.textTracks.addEventListener('change', this.trackChangeListener);
  14491. }
  14492. };
  14493. SubtitleTrackController.prototype.onMediaDetaching = function onMediaDetaching() {
  14494. if (!this.media) {
  14495. return;
  14496. }
  14497. if (this.useTextTrackPolling) {
  14498. clearInterval(this.subtitlePollingInterval);
  14499. } else {
  14500. this.media.textTracks.removeEventListener('change', this.trackChangeListener);
  14501. }
  14502. this.media = undefined;
  14503. };
  14504. // Reset subtitle tracks on manifest loading
  14505. SubtitleTrackController.prototype.onManifestLoading = function onManifestLoading() {
  14506. this.tracks = [];
  14507. this.trackId = -1;
  14508. };
  14509. // Fired whenever a new manifest is loaded.
  14510. SubtitleTrackController.prototype.onManifestLoaded = function onManifestLoaded(data) {
  14511. var _this3 = this;
  14512. var tracks = data.subtitles || [];
  14513. this.tracks = tracks;
  14514. this.trackId = -1;
  14515. this.hls.trigger(events["a" /* default */].SUBTITLE_TRACKS_UPDATED, {subtitleTracks: tracks});
  14516. // loop through available subtitle tracks and autoselect default if needed
  14517. // TODO: improve selection logic to handle forced, etc
  14518. tracks.forEach(function (track) {
  14519. if (track.default) {
  14520. // setting this.subtitleTrack will trigger internal logic
  14521. // if media has not been attached yet, it will fail
  14522. // we keep a reference to the default track id
  14523. // and we'll set subtitleTrack when onMediaAttached is triggered
  14524. if (_this3.media) {
  14525. _this3.subtitleTrack = track.id;
  14526. } else {
  14527. _this3.queuedDefaultTrack = track.id;
  14528. }
  14529. }
  14530. });
  14531. };
  14532. // Trigger subtitle track playlist reload.
  14533. SubtitleTrackController.prototype.onTick = function onTick() {
  14534. var trackId = this.trackId;
  14535. var subtitleTrack = this.tracks[trackId];
  14536. if (!subtitleTrack) {
  14537. return;
  14538. }
  14539. var details = subtitleTrack.details;
  14540. // check if we need to load playlist for this subtitle Track
  14541. if (details === undefined || details.live === true) {
  14542. // track not retrieved yet, or live playlist we need to (re)load it
  14543. logger["b" /* logger */].log('(re)loading playlist for subtitle track ' + trackId);
  14544. this.hls.trigger(events["a" /* default */].SUBTITLE_TRACK_LOADING, {
  14545. url: subtitleTrack.url,
  14546. id: trackId
  14547. });
  14548. }
  14549. };
  14550. SubtitleTrackController.prototype.onSubtitleTrackLoaded = function onSubtitleTrackLoaded(data) {
  14551. var _this4 = this;
  14552. if (data.id < this.tracks.length) {
  14553. logger["b" /* logger */].log('subtitle track ' + data.id + ' loaded');
  14554. this.tracks[data.id].details = data.details;
  14555. // check if current playlist is a live playlist
  14556. if (data.details.live && !this.timer) {
  14557. // if live playlist we will have to reload it periodically
  14558. // set reload period to playlist target duration
  14559. this.timer = setInterval(function () {
  14560. _this4.onTick();
  14561. }, 1000 * data.details.targetduration, this);
  14562. }
  14563. if (!data.details.live && this.timer) {
  14564. // playlist is not live and timer is armed : stopping it
  14565. clearInterval(this.timer);
  14566. this.timer = null;
  14567. }
  14568. }
  14569. };
  14570. /** get alternate subtitle tracks list from playlist **/
  14571. SubtitleTrackController.prototype.setSubtitleTrackInternal = function setSubtitleTrackInternal(newId) {
  14572. // check if level idx is valid
  14573. if (newId < -1 || newId >= this.tracks.length) {
  14574. return;
  14575. }
  14576. // stopping live reloading timer if any
  14577. if (this.timer) {
  14578. clearInterval(this.timer);
  14579. this.timer = null;
  14580. }
  14581. var textTracks = filterSubtitleTracks(this.media.textTracks);
  14582. // hide currently enabled subtitle track
  14583. if (this.trackId !== -1 && this.subtitleDisplay) {
  14584. textTracks[this.trackId].mode = 'hidden';
  14585. }
  14586. this.trackId = newId;
  14587. logger["b" /* logger */].log('switching to subtitle track ' + newId);
  14588. this.hls.trigger(events["a" /* default */].SUBTITLE_TRACK_SWITCH, {id: newId});
  14589. if (newId === -1) {
  14590. return;
  14591. }
  14592. var subtitleTrack = this.tracks[newId];
  14593. if (this.subtitleDisplay) {
  14594. textTracks[newId].mode = 'showing';
  14595. }
  14596. // check if we need to load playlist for this subtitle Track
  14597. var details = subtitleTrack.details;
  14598. if (details === undefined || details.live === true) {
  14599. // track not retrieved yet, or live playlist we need to (re)load it
  14600. logger["b" /* logger */].log('(re)loading playlist for subtitle track ' + newId);
  14601. this.hls.trigger(events["a" /* default */].SUBTITLE_TRACK_LOADING, {
  14602. url: subtitleTrack.url,
  14603. id: newId
  14604. });
  14605. }
  14606. };
  14607. subtitle_track_controller__createClass(SubtitleTrackController, [{
  14608. key: 'subtitleTracks',
  14609. get: function get() {
  14610. return this.tracks;
  14611. }
  14612. /** get index of the selected subtitle track (index in subtitle track lists) **/
  14613. }, {
  14614. key: 'subtitleTrack',
  14615. get: function get() {
  14616. return this.trackId;
  14617. }
  14618. /** select a subtitle track, based on its index in subtitle track lists**/
  14619. ,
  14620. set: function set(subtitleTrackId) {
  14621. if (this.trackId !== subtitleTrackId) {
  14622. // || this.tracks[subtitleTrackId].details === undefined) {
  14623. this.setSubtitleTrackInternal(subtitleTrackId);
  14624. }
  14625. }
  14626. }]);
  14627. return SubtitleTrackController;
  14628. }(event_handler);
  14629. /* harmony default export */
  14630. var subtitle_track_controller = (subtitle_track_controller_SubtitleTrackController);
  14631. // EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules
  14632. var decrypter = __webpack_require__(5);
  14633. // CONCATENATED MODULE: ./src/controller/subtitle-stream-controller.js
  14634. function subtitle_stream_controller__classCallCheck(instance, Constructor) {
  14635. if (!(instance instanceof Constructor)) {
  14636. throw new TypeError("Cannot call a class as a function");
  14637. }
  14638. }
  14639. function subtitle_stream_controller__possibleConstructorReturn(self, call) {
  14640. if (!self) {
  14641. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  14642. }
  14643. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  14644. }
  14645. function subtitle_stream_controller__inherits(subClass, superClass) {
  14646. if (typeof superClass !== "function" && superClass !== null) {
  14647. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  14648. }
  14649. subClass.prototype = Object.create(superClass && superClass.prototype, {
  14650. constructor: {
  14651. value: subClass,
  14652. enumerable: false,
  14653. writable: true,
  14654. configurable: true
  14655. }
  14656. });
  14657. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  14658. }
  14659. /*
  14660. * Subtitle Stream Controller
  14661. */
  14662. var subtitle_stream_controller_State = {
  14663. STOPPED: 'STOPPED',
  14664. IDLE: 'IDLE',
  14665. KEY_LOADING: 'KEY_LOADING',
  14666. FRAG_LOADING: 'FRAG_LOADING'
  14667. };
  14668. var subtitle_stream_controller_SubtitleStreamController = function (_TaskLoop) {
  14669. subtitle_stream_controller__inherits(SubtitleStreamController, _TaskLoop);
  14670. function SubtitleStreamController(hls) {
  14671. subtitle_stream_controller__classCallCheck(this, SubtitleStreamController);
  14672. var _this = subtitle_stream_controller__possibleConstructorReturn(this, _TaskLoop.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].ERROR, events["a" /* default */].KEY_LOADED, events["a" /* default */].FRAG_LOADED, events["a" /* default */].SUBTITLE_TRACKS_UPDATED, events["a" /* default */].SUBTITLE_TRACK_SWITCH, events["a" /* default */].SUBTITLE_TRACK_LOADED, events["a" /* default */].SUBTITLE_FRAG_PROCESSED));
  14673. _this.config = hls.config;
  14674. _this.vttFragSNsProcessed = {};
  14675. _this.vttFragQueues = undefined;
  14676. _this.currentlyProcessing = null;
  14677. _this.state = subtitle_stream_controller_State.STOPPED;
  14678. _this.currentTrackId = -1;
  14679. _this.decrypter = new decrypter["a" /* default */](hls.observer, hls.config);
  14680. return _this;
  14681. }
  14682. SubtitleStreamController.prototype.onHandlerDestroyed = function onHandlerDestroyed() {
  14683. this.state = subtitle_stream_controller_State.STOPPED;
  14684. };
  14685. // Remove all queued items and create a new, empty queue for each track.
  14686. SubtitleStreamController.prototype.clearVttFragQueues = function clearVttFragQueues() {
  14687. var _this2 = this;
  14688. this.vttFragQueues = {};
  14689. this.tracks.forEach(function (track) {
  14690. _this2.vttFragQueues[track.id] = [];
  14691. });
  14692. };
  14693. // If no frag is being processed and queue isn't empty, initiate processing of next frag in line.
  14694. SubtitleStreamController.prototype.nextFrag = function nextFrag() {
  14695. if (this.currentlyProcessing === null && this.currentTrackId > -1 && this.vttFragQueues[this.currentTrackId].length) {
  14696. var frag = this.currentlyProcessing = this.vttFragQueues[this.currentTrackId].shift();
  14697. this.fragCurrent = frag;
  14698. this.hls.trigger(events["a" /* default */].FRAG_LOADING, {frag: frag});
  14699. this.state = subtitle_stream_controller_State.FRAG_LOADING;
  14700. }
  14701. };
  14702. // When fragment has finished processing, add sn to list of completed if successful.
  14703. SubtitleStreamController.prototype.onSubtitleFragProcessed = function onSubtitleFragProcessed(data) {
  14704. if (data.success) {
  14705. this.vttFragSNsProcessed[data.frag.trackId].push(data.frag.sn);
  14706. }
  14707. this.currentlyProcessing = null;
  14708. this.state = subtitle_stream_controller_State.IDLE;
  14709. this.nextFrag();
  14710. };
  14711. SubtitleStreamController.prototype.onMediaAttached = function onMediaAttached() {
  14712. this.state = subtitle_stream_controller_State.IDLE;
  14713. };
  14714. // If something goes wrong, procede to next frag, if we were processing one.
  14715. SubtitleStreamController.prototype.onError = function onError(data) {
  14716. var frag = data.frag;
  14717. // don't handle frag error not related to subtitle fragment
  14718. if (frag && frag.type !== 'subtitle') {
  14719. return;
  14720. }
  14721. if (this.currentlyProcessing) {
  14722. this.currentlyProcessing = null;
  14723. this.nextFrag();
  14724. }
  14725. };
  14726. SubtitleStreamController.prototype.doTick = function doTick() {
  14727. var _this3 = this;
  14728. switch (this.state) {
  14729. case subtitle_stream_controller_State.IDLE:
  14730. var tracks = this.tracks;
  14731. var trackId = this.currentTrackId;
  14732. var processedFragSNs = this.vttFragSNsProcessed[trackId],
  14733. fragQueue = this.vttFragQueues[trackId],
  14734. currentFragSN = !!this.currentlyProcessing ? this.currentlyProcessing.sn : -1;
  14735. var alreadyProcessed = function alreadyProcessed(frag) {
  14736. return processedFragSNs.indexOf(frag.sn) > -1;
  14737. };
  14738. var alreadyInQueue = function alreadyInQueue(frag) {
  14739. return fragQueue.some(function (fragInQueue) {
  14740. return fragInQueue.sn === frag.sn;
  14741. });
  14742. };
  14743. // exit if tracks don't exist
  14744. if (!tracks) {
  14745. break;
  14746. }
  14747. var trackDetails;
  14748. if (trackId < tracks.length) {
  14749. trackDetails = tracks[trackId].details;
  14750. }
  14751. if (typeof trackDetails === 'undefined') {
  14752. break;
  14753. }
  14754. // Add all fragments that haven't been, aren't currently being and aren't waiting to be processed, to queue.
  14755. trackDetails.fragments.forEach(function (frag) {
  14756. if (!(alreadyProcessed(frag) || frag.sn === currentFragSN || alreadyInQueue(frag))) {
  14757. // Load key if subtitles are encrypted
  14758. if (frag.decryptdata && frag.decryptdata.uri != null && frag.decryptdata.key == null) {
  14759. logger["b" /* logger */].log('Loading key for ' + frag.sn);
  14760. _this3.state = subtitle_stream_controller_State.KEY_LOADING;
  14761. _this3.hls.trigger(events["a" /* default */].KEY_LOADING, {frag: frag});
  14762. } else {
  14763. // Frags don't know their subtitle track ID, so let's just add that...
  14764. frag.trackId = trackId;
  14765. fragQueue.push(frag);
  14766. _this3.nextFrag();
  14767. }
  14768. }
  14769. });
  14770. }
  14771. };
  14772. // Got all new subtitle tracks.
  14773. SubtitleStreamController.prototype.onSubtitleTracksUpdated = function onSubtitleTracksUpdated(data) {
  14774. var _this4 = this;
  14775. logger["b" /* logger */].log('subtitle tracks updated');
  14776. this.tracks = data.subtitleTracks;
  14777. this.clearVttFragQueues();
  14778. this.vttFragSNsProcessed = {};
  14779. this.tracks.forEach(function (track) {
  14780. _this4.vttFragSNsProcessed[track.id] = [];
  14781. });
  14782. };
  14783. SubtitleStreamController.prototype.onSubtitleTrackSwitch = function onSubtitleTrackSwitch(data) {
  14784. this.currentTrackId = data.id;
  14785. this.clearVttFragQueues();
  14786. };
  14787. // Got a new set of subtitle fragments.
  14788. SubtitleStreamController.prototype.onSubtitleTrackLoaded = function onSubtitleTrackLoaded() {
  14789. this.tick();
  14790. };
  14791. SubtitleStreamController.prototype.onKeyLoaded = function onKeyLoaded() {
  14792. if (this.state === subtitle_stream_controller_State.KEY_LOADING) {
  14793. this.state = subtitle_stream_controller_State.IDLE;
  14794. this.tick();
  14795. }
  14796. };
  14797. SubtitleStreamController.prototype.onFragLoaded = function onFragLoaded(data) {
  14798. var fragCurrent = this.fragCurrent,
  14799. decryptData = data.frag.decryptdata;
  14800. var fragLoaded = data.frag,
  14801. hls = this.hls;
  14802. if (this.state === subtitle_stream_controller_State.FRAG_LOADING && fragCurrent && data.frag.type === 'subtitle' && fragCurrent.sn === data.frag.sn) {
  14803. // check to see if the payload needs to be decrypted
  14804. if (data.payload.byteLength > 0 && decryptData != null && decryptData.key != null && decryptData.method === 'AES-128') {
  14805. var startTime;
  14806. try {
  14807. startTime = performance.now();
  14808. } catch (error) {
  14809. startTime = Date.now();
  14810. }
  14811. // decrypt the subtitles
  14812. this.decrypter.decrypt(data.payload, decryptData.key.buffer, decryptData.iv.buffer, function (decryptedData) {
  14813. var endTime;
  14814. try {
  14815. endTime = performance.now();
  14816. } catch (error) {
  14817. endTime = Date.now();
  14818. }
  14819. hls.trigger(events["a" /* default */].FRAG_DECRYPTED, {
  14820. frag: fragLoaded,
  14821. payload: decryptedData,
  14822. stats: {tstart: startTime, tdecrypt: endTime}
  14823. });
  14824. });
  14825. }
  14826. }
  14827. };
  14828. return SubtitleStreamController;
  14829. }(task_loop);
  14830. /* harmony default export */
  14831. var subtitle_stream_controller = (subtitle_stream_controller_SubtitleStreamController);
  14832. // CONCATENATED MODULE: ./src/controller/eme-controller.js
  14833. var eme_controller__createClass = function () {
  14834. function defineProperties(target, props) {
  14835. for (var i = 0; i < props.length; i++) {
  14836. var descriptor = props[i];
  14837. descriptor.enumerable = descriptor.enumerable || false;
  14838. descriptor.configurable = true;
  14839. if ("value" in descriptor) descriptor.writable = true;
  14840. Object.defineProperty(target, descriptor.key, descriptor);
  14841. }
  14842. }
  14843. return function (Constructor, protoProps, staticProps) {
  14844. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  14845. if (staticProps) defineProperties(Constructor, staticProps);
  14846. return Constructor;
  14847. };
  14848. }();
  14849. function eme_controller__classCallCheck(instance, Constructor) {
  14850. if (!(instance instanceof Constructor)) {
  14851. throw new TypeError("Cannot call a class as a function");
  14852. }
  14853. }
  14854. function eme_controller__possibleConstructorReturn(self, call) {
  14855. if (!self) {
  14856. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  14857. }
  14858. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  14859. }
  14860. function eme_controller__inherits(subClass, superClass) {
  14861. if (typeof superClass !== "function" && superClass !== null) {
  14862. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  14863. }
  14864. subClass.prototype = Object.create(superClass && superClass.prototype, {
  14865. constructor: {
  14866. value: subClass,
  14867. enumerable: false,
  14868. writable: true,
  14869. configurable: true
  14870. }
  14871. });
  14872. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  14873. }
  14874. /**
  14875. * @author Stephan Hesse <disparat@gmail.com> | <tchakabam@gmail.com>
  14876. *
  14877. * DRM support for Hls.js
  14878. */
  14879. var MAX_LICENSE_REQUEST_FAILURES = 3;
  14880. /**
  14881. * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess
  14882. */
  14883. var KeySystems = {
  14884. WIDEVINE: 'com.widevine.alpha',
  14885. PLAYREADY: 'com.microsoft.playready'
  14886. };
  14887. /**
  14888. * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration
  14889. * @param {Array<string>} audioCodecs List of required audio codecs to support
  14890. * @param {Array<string>} videoCodecs List of required video codecs to support
  14891. * @param {object} drmSystemOptions Optional parameters/requirements for the key-system
  14892. * @returns {Array<MediaSystemConfiguration>} An array of supported configurations
  14893. */
  14894. var createWidevineMediaKeySystemConfigurations = function createWidevineMediaKeySystemConfigurations(audioCodecs, videoCodecs, drmSystemOptions) {
  14895. /* jshint ignore:line */
  14896. var baseConfig = {
  14897. //initDataTypes: ['keyids', 'mp4'],
  14898. //label: "",
  14899. //persistentState: "not-allowed", // or "required" ?
  14900. //distinctiveIdentifier: "not-allowed", // or "required" ?
  14901. //sessionTypes: ['temporary'],
  14902. videoCapabilities: [
  14903. //{ contentType: 'video/mp4; codecs="avc1.42E01E"' }
  14904. ]
  14905. };
  14906. videoCodecs.forEach(function (codec) {
  14907. baseConfig.videoCapabilities.push({
  14908. contentType: 'video/mp4; codecs="' + codec + '"'
  14909. });
  14910. });
  14911. return [baseConfig];
  14912. };
  14913. /**
  14914. * The idea here is to handle key-system (and their respective platforms) specific configuration differences
  14915. * in order to work with the local requestMediaKeySystemAccess method.
  14916. *
  14917. * We can also rule-out platform-related key-system support at this point by throwing an error or returning null.
  14918. *
  14919. * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum
  14920. * @param {Array<string>} audioCodecs List of required audio codecs to support
  14921. * @param {Array<string>} videoCodecs List of required video codecs to support
  14922. * @returns {Array<MediaSystemConfiguration> | null} A non-empty Array of MediaKeySystemConfiguration objects or `null`
  14923. */
  14924. var getSupportedMediaKeySystemConfigurations = function getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs) {
  14925. switch (keySystem) {
  14926. case KeySystems.WIDEVINE:
  14927. return createWidevineMediaKeySystemConfigurations(audioCodecs, videoCodecs);
  14928. default:
  14929. throw Error('Unknown key-system: ' + keySystem);
  14930. }
  14931. };
  14932. /**
  14933. * Controller to deal with encrypted media extensions (EME)
  14934. * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API
  14935. *
  14936. * @class
  14937. * @constructor
  14938. */
  14939. var eme_controller_EMEController = function (_EventHandler) {
  14940. eme_controller__inherits(EMEController, _EventHandler);
  14941. /**
  14942. * @constructs
  14943. * @param {Hls} hls Our Hls.js instance
  14944. */
  14945. function EMEController(hls) {
  14946. eme_controller__classCallCheck(this, EMEController);
  14947. var _this = eme_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHED, events["a" /* default */].MANIFEST_PARSED));
  14948. _this._widevineLicenseUrl = hls.config.widevineLicenseUrl;
  14949. _this._licenseXhrSetup = hls.config.licenseXhrSetup;
  14950. _this._emeEnabled = hls.config.emeEnabled;
  14951. _this._requestMediaKeySystemAccess = hls.config.requestMediaKeySystemAccessFunc;
  14952. _this._mediaKeysList = [];
  14953. _this._media = null;
  14954. _this._hasSetMediaKeys = false;
  14955. _this._isMediaEncrypted = false;
  14956. _this._requestLicenseFailureCount = 0;
  14957. return _this;
  14958. }
  14959. /**
  14960. *
  14961. * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum
  14962. * @returns {string} License server URL for key-system (if any configured, otherwise causes error)
  14963. */
  14964. EMEController.prototype.getLicenseServerUrl = function getLicenseServerUrl(keySystem) {
  14965. var url = void 0;
  14966. switch (keySystem) {
  14967. case KeySystems.WIDEVINE:
  14968. url = this._widevineLicenseUrl;
  14969. break;
  14970. default:
  14971. url = null;
  14972. break;
  14973. }
  14974. if (!url) {
  14975. logger["b" /* logger */].error('No license server URL configured for key-system "' + keySystem + '"');
  14976. this.hls.trigger(events["a" /* default */].ERROR, {
  14977. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  14978. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_LICENSE_REQUEST_FAILED,
  14979. fatal: true
  14980. });
  14981. }
  14982. return url;
  14983. };
  14984. /**
  14985. * Requests access object and adds it to our list upon success
  14986. * @private
  14987. * @param {string} keySystem System ID (see `KeySystems`)
  14988. * @param {Array<string>} audioCodecs List of required audio codecs to support
  14989. * @param {Array<string>} videoCodecs List of required video codecs to support
  14990. */
  14991. EMEController.prototype._attemptKeySystemAccess = function _attemptKeySystemAccess(keySystem, audioCodecs, videoCodecs) {
  14992. var _this2 = this;
  14993. // TODO: add other DRM "options"
  14994. var mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs);
  14995. if (!mediaKeySystemConfigs) {
  14996. logger["b" /* logger */].warn('Can not create config for key-system (maybe because platform is not supported):', keySystem);
  14997. return;
  14998. }
  14999. logger["b" /* logger */].log('Requesting encrypted media key-system access');
  15000. // expecting interface like window.navigator.requestMediaKeySystemAccess
  15001. this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs).then(function (mediaKeySystemAccess) {
  15002. _this2._onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess);
  15003. }).catch(function (err) {
  15004. logger["b" /* logger */].error('Failed to obtain key-system "' + keySystem + '" access:', err);
  15005. });
  15006. };
  15007. /**
  15008. * Handles obtaining access to a key-system
  15009. *
  15010. * @param {string} keySystem
  15011. * @param {MediaKeySystemAccess} mediaKeySystemAccess https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemAccess
  15012. */
  15013. EMEController.prototype._onMediaKeySystemAccessObtained = function _onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess) {
  15014. var _this3 = this;
  15015. logger["b" /* logger */].log('Access for key-system "' + keySystem + '" obtained');
  15016. var mediaKeysListItem = {
  15017. mediaKeys: null,
  15018. mediaKeysSession: null,
  15019. mediaKeysSessionInitialized: false,
  15020. mediaKeySystemAccess: mediaKeySystemAccess,
  15021. mediaKeySystemDomain: keySystem
  15022. };
  15023. this._mediaKeysList.push(mediaKeysListItem);
  15024. mediaKeySystemAccess.createMediaKeys().then(function (mediaKeys) {
  15025. mediaKeysListItem.mediaKeys = mediaKeys;
  15026. logger["b" /* logger */].log('Media-keys created for key-system "' + keySystem + '"');
  15027. _this3._onMediaKeysCreated();
  15028. }).catch(function (err) {
  15029. logger["b" /* logger */].error('Failed to create media-keys:', err);
  15030. });
  15031. };
  15032. /**
  15033. * Handles key-creation (represents access to CDM). We are going to create key-sessions upon this
  15034. * for all existing keys where no session exists yet.
  15035. */
  15036. EMEController.prototype._onMediaKeysCreated = function _onMediaKeysCreated() {
  15037. var _this4 = this;
  15038. // check for all key-list items if a session exists, otherwise, create one
  15039. this._mediaKeysList.forEach(function (mediaKeysListItem) {
  15040. if (!mediaKeysListItem.mediaKeysSession) {
  15041. mediaKeysListItem.mediaKeysSession = mediaKeysListItem.mediaKeys.createSession();
  15042. _this4._onNewMediaKeySession(mediaKeysListItem.mediaKeysSession);
  15043. }
  15044. });
  15045. };
  15046. /**
  15047. *
  15048. * @param {*} keySession
  15049. */
  15050. EMEController.prototype._onNewMediaKeySession = function _onNewMediaKeySession(keySession) {
  15051. var _this5 = this;
  15052. logger["b" /* logger */].log('New key-system session ' + keySession.sessionId);
  15053. keySession.addEventListener('message', function (event) {
  15054. _this5._onKeySessionMessage(keySession, event.message);
  15055. }, false);
  15056. };
  15057. EMEController.prototype._onKeySessionMessage = function _onKeySessionMessage(keySession, message) {
  15058. logger["b" /* logger */].log('Got EME message event, creating license request');
  15059. this._requestLicense(message, function (data) {
  15060. logger["b" /* logger */].log('Received license data, updating key-session');
  15061. keySession.update(data);
  15062. });
  15063. };
  15064. EMEController.prototype._onMediaEncrypted = function _onMediaEncrypted(initDataType, initData) {
  15065. logger["b" /* logger */].log('Media is encrypted using "' + initDataType + '" init data type');
  15066. this._isMediaEncrypted = true;
  15067. this._mediaEncryptionInitDataType = initDataType;
  15068. this._mediaEncryptionInitData = initData;
  15069. this._attemptSetMediaKeys();
  15070. this._generateRequestWithPreferredKeySession();
  15071. };
  15072. EMEController.prototype._attemptSetMediaKeys = function _attemptSetMediaKeys() {
  15073. if (!this._hasSetMediaKeys) {
  15074. // FIXME: see if we can/want/need-to really to deal with several potential key-sessions?
  15075. var keysListItem = this._mediaKeysList[0];
  15076. if (!keysListItem || !keysListItem.mediaKeys) {
  15077. logger["b" /* logger */].error('Fatal: Media is encrypted but no CDM access or no keys have been obtained yet');
  15078. this.hls.trigger(events["a" /* default */].ERROR, {
  15079. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15080. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_NO_KEYS,
  15081. fatal: true
  15082. });
  15083. return;
  15084. }
  15085. logger["b" /* logger */].log('Setting keys for encrypted media');
  15086. this._media.setMediaKeys(keysListItem.mediaKeys);
  15087. this._hasSetMediaKeys = true;
  15088. }
  15089. };
  15090. EMEController.prototype._generateRequestWithPreferredKeySession = function _generateRequestWithPreferredKeySession() {
  15091. var _this6 = this;
  15092. // FIXME: see if we can/want/need-to really to deal with several potential key-sessions?
  15093. var keysListItem = this._mediaKeysList[0];
  15094. if (!keysListItem) {
  15095. logger["b" /* logger */].error('Fatal: Media is encrypted but not any key-system access has been obtained yet');
  15096. this.hls.trigger(events["a" /* default */].ERROR, {
  15097. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15098. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_NO_ACCESS,
  15099. fatal: true
  15100. });
  15101. return;
  15102. }
  15103. if (keysListItem.mediaKeysSessionInitialized) {
  15104. logger["b" /* logger */].warn('Key-Session already initialized but requested again');
  15105. return;
  15106. }
  15107. var keySession = keysListItem.mediaKeysSession;
  15108. if (!keySession) {
  15109. logger["b" /* logger */].error('Fatal: Media is encrypted but no key-session existing');
  15110. this.hls.trigger(events["a" /* default */].ERROR, {
  15111. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15112. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_NO_SESSION,
  15113. fatal: true
  15114. });
  15115. }
  15116. var initDataType = this._mediaEncryptionInitDataType;
  15117. var initData = this._mediaEncryptionInitData;
  15118. logger["b" /* logger */].log('Generating key-session request for "' + initDataType + '" init data type');
  15119. keysListItem.mediaKeysSessionInitialized = true;
  15120. keySession.generateRequest(initDataType, initData).then(function () {
  15121. logger["b" /* logger */].debug('Key-session generation succeeded');
  15122. }).catch(function (err) {
  15123. logger["b" /* logger */].error('Error generating key-session request:', err);
  15124. _this6.hls.trigger(events["a" /* default */].ERROR, {
  15125. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15126. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_NO_SESSION,
  15127. fatal: false
  15128. });
  15129. });
  15130. };
  15131. /**
  15132. * @param {string} url License server URL
  15133. * @param {ArrayBuffer} keyMessage Message data issued by key-system
  15134. * @param {function} callback Called when XHR has succeeded
  15135. * @returns {XMLHttpRequest} Unsent (but opened state) XHR object
  15136. */
  15137. EMEController.prototype._createLicenseXhr = function _createLicenseXhr(url, keyMessage, callback) {
  15138. var xhr = new XMLHttpRequest();
  15139. var licenseXhrSetup = this._licenseXhrSetup;
  15140. try {
  15141. if (licenseXhrSetup) {
  15142. try {
  15143. licenseXhrSetup(xhr, url);
  15144. } catch (e) {
  15145. // let's try to open before running setup
  15146. xhr.open('POST', url, true);
  15147. licenseXhrSetup(xhr, url);
  15148. }
  15149. }
  15150. // if licenseXhrSetup did not yet call open, let's do it now
  15151. if (!xhr.readyState) {
  15152. xhr.open('POST', url, true);
  15153. }
  15154. } catch (e) {
  15155. // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
  15156. logger["b" /* logger */].error('Error setting up key-system license XHR', e);
  15157. this.hls.trigger(events["a" /* default */].ERROR, {
  15158. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15159. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_LICENSE_REQUEST_FAILED,
  15160. fatal: true
  15161. });
  15162. return;
  15163. }
  15164. xhr.responseType = 'arraybuffer';
  15165. xhr.onreadystatechange = this._onLicenseRequestReadyStageChange.bind(this, xhr, url, keyMessage, callback);
  15166. return xhr;
  15167. };
  15168. /**
  15169. * @param {XMLHttpRequest} xhr
  15170. * @param {string} url License server URL
  15171. * @param {ArrayBuffer} keyMessage Message data issued by key-system
  15172. * @param {function} callback Called when XHR has succeeded
  15173. *
  15174. */
  15175. EMEController.prototype._onLicenseRequestReadyStageChange = function _onLicenseRequestReadyStageChange(xhr, url, keyMessage, callback) {
  15176. switch (xhr.readyState) {
  15177. case 4:
  15178. if (xhr.status === 200) {
  15179. this._requestLicenseFailureCount = 0;
  15180. logger["b" /* logger */].log('License request succeeded');
  15181. callback(xhr.response);
  15182. } else {
  15183. logger["b" /* logger */].error('License Request XHR failed (' + url + '). Status: ' + xhr.status + ' (' + xhr.statusText + ')');
  15184. this._requestLicenseFailureCount++;
  15185. if (this._requestLicenseFailureCount <= MAX_LICENSE_REQUEST_FAILURES) {
  15186. var attemptsLeft = MAX_LICENSE_REQUEST_FAILURES - this._requestLicenseFailureCount + 1;
  15187. logger["b" /* logger */].warn('Retrying license request, ' + attemptsLeft + ' attempts left');
  15188. this._requestLicense(keyMessage, callback);
  15189. return;
  15190. }
  15191. this.hls.trigger(events["a" /* default */].ERROR, {
  15192. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15193. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_LICENSE_REQUEST_FAILED,
  15194. fatal: true
  15195. });
  15196. }
  15197. break;
  15198. }
  15199. };
  15200. /**
  15201. * @param {object} keysListItem
  15202. * @param {ArrayBuffer} keyMessage
  15203. * @returns {ArrayBuffer} Challenge data posted to license server
  15204. */
  15205. EMEController.prototype._generateLicenseRequestChallenge = function _generateLicenseRequestChallenge(keysListItem, keyMessage) {
  15206. var challenge = void 0;
  15207. if (keysListItem.mediaKeySystemDomain === KeySystems.PLAYREADY) {
  15208. logger["b" /* logger */].error('PlayReady is not supported (yet)');
  15209. // from https://github.com/MicrosoftEdge/Demos/blob/master/eme/scripts/demo.js
  15210. /*
  15211. if (this.licenseType !== this.LICENSE_TYPE_WIDEVINE) {
  15212. // For PlayReady CDMs, we need to dig the Challenge out of the XML.
  15213. var keyMessageXml = new DOMParser().parseFromString(String.fromCharCode.apply(null, new Uint16Array(keyMessage)), 'application/xml');
  15214. if (keyMessageXml.getElementsByTagName('Challenge')[0]) {
  15215. challenge = atob(keyMessageXml.getElementsByTagName('Challenge')[0].childNodes[0].nodeValue);
  15216. } else {
  15217. throw 'Cannot find <Challenge> in key message';
  15218. }
  15219. var headerNames = keyMessageXml.getElementsByTagName('name');
  15220. var headerValues = keyMessageXml.getElementsByTagName('value');
  15221. if (headerNames.length !== headerValues.length) {
  15222. throw 'Mismatched header <name>/<value> pair in key message';
  15223. }
  15224. for (var i = 0; i < headerNames.length; i++) {
  15225. xhr.setRequestHeader(headerNames[i].childNodes[0].nodeValue, headerValues[i].childNodes[0].nodeValue);
  15226. }
  15227. }
  15228. */
  15229. } else if (keysListItem.mediaKeySystemDomain === KeySystems.WIDEVINE) {
  15230. // For Widevine CDMs, the challenge is the keyMessage.
  15231. challenge = keyMessage;
  15232. } else {
  15233. logger["b" /* logger */].error('Unsupported key-system:', keysListItem.mediaKeySystemDomain);
  15234. }
  15235. return challenge;
  15236. };
  15237. EMEController.prototype._requestLicense = function _requestLicense(keyMessage, callback) {
  15238. logger["b" /* logger */].log('Requesting content license for key-system');
  15239. var keysListItem = this._mediaKeysList[0];
  15240. if (!keysListItem) {
  15241. logger["b" /* logger */].error('Fatal error: Media is encrypted but no key-system access has been obtained yet');
  15242. this.hls.trigger(events["a" /* default */].ERROR, {
  15243. type: errors["b" /* ErrorTypes */].KEY_SYSTEM_ERROR,
  15244. details: errors["a" /* ErrorDetails */].KEY_SYSTEM_NO_ACCESS,
  15245. fatal: true
  15246. });
  15247. return;
  15248. }
  15249. var url = this.getLicenseServerUrl(keysListItem.mediaKeySystemDomain);
  15250. var xhr = this._createLicenseXhr(url, keyMessage, callback);
  15251. logger["b" /* logger */].log('Sending license request to URL: ' + url);
  15252. xhr.send(this._generateLicenseRequestChallenge(keysListItem, keyMessage));
  15253. };
  15254. EMEController.prototype.onMediaAttached = function onMediaAttached(data) {
  15255. var _this7 = this;
  15256. if (!this._emeEnabled) {
  15257. return;
  15258. }
  15259. var media = data.media;
  15260. // keep reference of media
  15261. this._media = media;
  15262. // FIXME: also handle detaching media !
  15263. media.addEventListener('encrypted', function (e) {
  15264. _this7._onMediaEncrypted(e.initDataType, e.initData);
  15265. });
  15266. };
  15267. EMEController.prototype.onManifestParsed = function onManifestParsed(data) {
  15268. if (!this._emeEnabled) {
  15269. return;
  15270. }
  15271. var audioCodecs = data.levels.map(function (level) {
  15272. return level.audioCodec;
  15273. });
  15274. var videoCodecs = data.levels.map(function (level) {
  15275. return level.videoCodec;
  15276. });
  15277. this._attemptKeySystemAccess(KeySystems.WIDEVINE, audioCodecs, videoCodecs);
  15278. };
  15279. eme_controller__createClass(EMEController, [{
  15280. key: 'requestMediaKeySystemAccess',
  15281. get: function get() {
  15282. if (!this._requestMediaKeySystemAccess) {
  15283. throw new Error('No requestMediaKeySystemAccess function configured');
  15284. }
  15285. return this._requestMediaKeySystemAccess;
  15286. }
  15287. }]);
  15288. return EMEController;
  15289. }(event_handler);
  15290. /* harmony default export */
  15291. var eme_controller = (eme_controller_EMEController);
  15292. // CONCATENATED MODULE: ./src/helper/mediakeys-helper.js
  15293. var requestMediaKeySystemAccess = function () {
  15294. if (window.navigator && window.navigator.requestMediaKeySystemAccess) {
  15295. return window.navigator.requestMediaKeySystemAccess.bind(window.navigator);
  15296. } else {
  15297. return null;
  15298. }
  15299. }();
  15300. // CONCATENATED MODULE: ./src/config.js
  15301. /**
  15302. * HLS config
  15303. */
  15304. //import FetchLoader from './utils/fetch-loader';
  15305. var hlsDefaultConfig = {
  15306. autoStartLoad: true, // used by stream-controller
  15307. startPosition: -1, // used by stream-controller
  15308. defaultAudioCodec: undefined, // used by stream-controller
  15309. debug: false, // used by logger
  15310. capLevelOnFPSDrop: false, // used by fps-controller
  15311. capLevelToPlayerSize: false, // used by cap-level-controller
  15312. initialLiveManifestSize: 1, // used by stream-controller
  15313. maxBufferLength: 30, // used by stream-controller
  15314. maxBufferSize: 60 * 1000 * 1000, // used by stream-controller
  15315. maxBufferHole: 0.5, // used by stream-controller
  15316. maxSeekHole: 2, // used by stream-controller
  15317. lowBufferWatchdogPeriod: 0.5, // used by stream-controller
  15318. highBufferWatchdogPeriod: 3, // used by stream-controller
  15319. nudgeOffset: 0.1, // used by stream-controller
  15320. nudgeMaxRetry: 3, // used by stream-controller
  15321. maxFragLookUpTolerance: 0.25, // used by stream-controller
  15322. liveSyncDurationCount: 3, // used by stream-controller
  15323. liveMaxLatencyDurationCount: Infinity, // used by stream-controller
  15324. liveSyncDuration: undefined, // used by stream-controller
  15325. liveMaxLatencyDuration: undefined, // used by stream-controller
  15326. liveDurationInfinity: false, // used by buffer-controller
  15327. maxMaxBufferLength: 600, // used by stream-controller
  15328. enableWorker: true, // used by demuxer
  15329. enableSoftwareAES: true, // used by decrypter
  15330. manifestLoadingTimeOut: 10000, // used by playlist-loader
  15331. manifestLoadingMaxRetry: 1, // used by playlist-loader
  15332. manifestLoadingRetryDelay: 1000, // used by playlist-loader
  15333. manifestLoadingMaxRetryTimeout: 64000, // used by playlist-loader
  15334. startLevel: undefined, // used by level-controller
  15335. levelLoadingTimeOut: 10000, // used by playlist-loader
  15336. levelLoadingMaxRetry: 4, // used by playlist-loader
  15337. levelLoadingRetryDelay: 1000, // used by playlist-loader
  15338. levelLoadingMaxRetryTimeout: 64000, // used by playlist-loader
  15339. fragLoadingTimeOut: 20000, // used by fragment-loader
  15340. fragLoadingMaxRetry: 6, // used by fragment-loader
  15341. fragLoadingRetryDelay: 1000, // used by fragment-loader
  15342. fragLoadingMaxRetryTimeout: 64000, // used by fragment-loader
  15343. fragLoadingLoopThreshold: 3, // used by stream-controller
  15344. startFragPrefetch: false, // used by stream-controller
  15345. fpsDroppedMonitoringPeriod: 5000, // used by fps-controller
  15346. fpsDroppedMonitoringThreshold: 0.2, // used by fps-controller
  15347. appendErrorMaxRetry: 3, // used by buffer-controller
  15348. loader: xhr_loader,
  15349. //loader: FetchLoader,
  15350. fLoader: undefined,
  15351. pLoader: undefined,
  15352. xhrSetup: undefined,
  15353. licenseXhrSetup: undefined, // used by eme-controller
  15354. fetchSetup: undefined,
  15355. abrController: abr_controller,
  15356. bufferController: buffer_controller,
  15357. capLevelController: cap_level_controller,
  15358. fpsController: fps_controller,
  15359. stretchShortVideoTrack: false, // used by mp4-remuxer
  15360. maxAudioFramesDrift: 1, // used by mp4-remuxer
  15361. forceKeyFrameOnDiscontinuity: true, // used by ts-demuxer
  15362. abrEwmaFastLive: 3, // used by abr-controller
  15363. abrEwmaSlowLive: 9, // used by abr-controller
  15364. abrEwmaFastVoD: 3, // used by abr-controller
  15365. abrEwmaSlowVoD: 9, // used by abr-controller
  15366. abrEwmaDefaultEstimate: 5e5, // 500 kbps // used by abr-controller
  15367. abrBandWidthFactor: 0.95, // used by abr-controller
  15368. abrBandWidthUpFactor: 0.7, // used by abr-controller
  15369. abrMaxWithRealBitrate: false, // used by abr-controller
  15370. maxStarvationDelay: 4, // used by abr-controller
  15371. maxLoadingDelay: 4, // used by abr-controller
  15372. minAutoBitrate: 0, // used by hls
  15373. emeEnabled: false, // used by eme-controller
  15374. widevineLicenseUrl: undefined, // used by eme-controller
  15375. requestMediaKeySystemAccessFunc: requestMediaKeySystemAccess // used by eme-controller
  15376. };
  15377. if (true) {
  15378. hlsDefaultConfig.subtitleStreamController = subtitle_stream_controller;
  15379. hlsDefaultConfig.subtitleTrackController = subtitle_track_controller;
  15380. hlsDefaultConfig.timelineController = timeline_controller;
  15381. hlsDefaultConfig.cueHandler = cues_namespaceObject;
  15382. hlsDefaultConfig.enableCEA708Captions = true; // used by timeline-controller
  15383. hlsDefaultConfig.enableWebVTT = true; // used by timeline-controller
  15384. hlsDefaultConfig.captionsTextTrack1Label = 'English'; // used by timeline-controller
  15385. hlsDefaultConfig.captionsTextTrack1LanguageCode = 'en'; // used by timeline-controller
  15386. hlsDefaultConfig.captionsTextTrack2Label = 'Spanish'; // used by timeline-controller
  15387. hlsDefaultConfig.captionsTextTrack2LanguageCode = 'es'; // used by timeline-controller
  15388. }
  15389. if (true) {
  15390. hlsDefaultConfig.audioStreamController = audio_stream_controller;
  15391. hlsDefaultConfig.audioTrackController = audio_track_controller;
  15392. }
  15393. if (true) {
  15394. hlsDefaultConfig.emeController = eme_controller;
  15395. }
  15396. // CONCATENATED MODULE: ./src/hls.js
  15397. var hls__createClass = function () {
  15398. function defineProperties(target, props) {
  15399. for (var i = 0; i < props.length; i++) {
  15400. var descriptor = props[i];
  15401. descriptor.enumerable = descriptor.enumerable || false;
  15402. descriptor.configurable = true;
  15403. if ("value" in descriptor) descriptor.writable = true;
  15404. Object.defineProperty(target, descriptor.key, descriptor);
  15405. }
  15406. }
  15407. return function (Constructor, protoProps, staticProps) {
  15408. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  15409. if (staticProps) defineProperties(Constructor, staticProps);
  15410. return Constructor;
  15411. };
  15412. }();
  15413. function hls__classCallCheck(instance, Constructor) {
  15414. if (!(instance instanceof Constructor)) {
  15415. throw new TypeError("Cannot call a class as a function");
  15416. }
  15417. }
  15418. /**
  15419. * HLS interface
  15420. */
  15421. // polyfill for IE11
  15422. __webpack_require__(12);
  15423. var hls_Hls = function () {
  15424. Hls.isSupported = function isSupported() {
  15425. return is_supported_isSupported();
  15426. };
  15427. hls__createClass(Hls, null, [{
  15428. key: 'version',
  15429. get: function get() {
  15430. return "0.8.9";
  15431. }
  15432. }, {
  15433. key: 'Events',
  15434. get: function get() {
  15435. return events["a" /* default */];
  15436. }
  15437. }, {
  15438. key: 'ErrorTypes',
  15439. get: function get() {
  15440. return errors["b" /* ErrorTypes */];
  15441. }
  15442. }, {
  15443. key: 'ErrorDetails',
  15444. get: function get() {
  15445. return errors["a" /* ErrorDetails */];
  15446. }
  15447. }, {
  15448. key: 'DefaultConfig',
  15449. get: function get() {
  15450. if (!Hls.defaultConfig) {
  15451. return hlsDefaultConfig;
  15452. }
  15453. return Hls.defaultConfig;
  15454. },
  15455. set: function set(defaultConfig) {
  15456. Hls.defaultConfig = defaultConfig;
  15457. }
  15458. }]);
  15459. function Hls() {
  15460. var _this = this;
  15461. var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  15462. hls__classCallCheck(this, Hls);
  15463. var defaultConfig = Hls.DefaultConfig;
  15464. if ((config.liveSyncDurationCount || config.liveMaxLatencyDurationCount) && (config.liveSyncDuration || config.liveMaxLatencyDuration)) {
  15465. throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration');
  15466. }
  15467. for (var prop in defaultConfig) {
  15468. if (prop in config) {
  15469. continue;
  15470. }
  15471. config[prop] = defaultConfig[prop];
  15472. }
  15473. if (config.liveMaxLatencyDurationCount !== undefined && config.liveMaxLatencyDurationCount <= config.liveSyncDurationCount) {
  15474. throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"');
  15475. }
  15476. if (config.liveMaxLatencyDuration !== undefined && (config.liveMaxLatencyDuration <= config.liveSyncDuration || config.liveSyncDuration === undefined)) {
  15477. throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"');
  15478. }
  15479. Object(logger["a" /* enableLogs */])(config.debug);
  15480. this.config = config;
  15481. this._autoLevelCapping = -1;
  15482. // observer setup
  15483. var observer = this.observer = new events_default.a();
  15484. observer.trigger = function trigger(event) {
  15485. for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  15486. data[_key - 1] = arguments[_key];
  15487. }
  15488. observer.emit.apply(observer, [event, event].concat(data));
  15489. };
  15490. observer.off = function off(event) {
  15491. for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  15492. data[_key2 - 1] = arguments[_key2];
  15493. }
  15494. observer.removeListener.apply(observer, [event].concat(data));
  15495. };
  15496. this.on = observer.on.bind(observer);
  15497. this.off = observer.off.bind(observer);
  15498. this.trigger = observer.trigger.bind(observer);
  15499. // core controllers and network loaders
  15500. var abrController = this.abrController = new config.abrController(this);
  15501. var bufferController = new config.bufferController(this);
  15502. var capLevelController = new config.capLevelController(this);
  15503. var fpsController = new config.fpsController(this);
  15504. var playListLoader = new playlist_loader(this);
  15505. var fragmentLoader = new fragment_loader(this);
  15506. var keyLoader = new key_loader(this);
  15507. var id3TrackController = new id3_track_controller(this);
  15508. // network controllers
  15509. var levelController = this.levelController = new level_controller(this);
  15510. var streamController = this.streamController = new stream_controller(this);
  15511. var networkControllers = [levelController, streamController];
  15512. // optional audio stream controller
  15513. var Controller = config.audioStreamController;
  15514. if (Controller) {
  15515. networkControllers.push(new Controller(this));
  15516. }
  15517. this.networkControllers = networkControllers;
  15518. var coreComponents = [playListLoader, fragmentLoader, keyLoader, abrController, bufferController, capLevelController, fpsController, id3TrackController];
  15519. // optional audio track and subtitle controller
  15520. Controller = config.audioTrackController;
  15521. if (Controller) {
  15522. var audioTrackController = new Controller(this);
  15523. this.audioTrackController = audioTrackController;
  15524. coreComponents.push(audioTrackController);
  15525. }
  15526. Controller = config.subtitleTrackController;
  15527. if (Controller) {
  15528. var subtitleTrackController = new Controller(this);
  15529. this.subtitleTrackController = subtitleTrackController;
  15530. coreComponents.push(subtitleTrackController);
  15531. }
  15532. Controller = config.emeController;
  15533. if (Controller) {
  15534. var emeController = new Controller(this);
  15535. this.emeController = emeController;
  15536. coreComponents.push(emeController);
  15537. }
  15538. // optional subtitle controller
  15539. [config.subtitleStreamController, config.timelineController].forEach(function (Controller) {
  15540. if (Controller) {
  15541. coreComponents.push(new Controller(_this));
  15542. }
  15543. });
  15544. this.coreComponents = coreComponents;
  15545. }
  15546. Hls.prototype.destroy = function destroy() {
  15547. logger["b" /* logger */].log('destroy');
  15548. this.trigger(events["a" /* default */].DESTROYING);
  15549. this.detachMedia();
  15550. this.coreComponents.concat(this.networkControllers).forEach(function (component) {
  15551. component.destroy();
  15552. });
  15553. this.url = null;
  15554. this.observer.removeAllListeners();
  15555. this._autoLevelCapping = -1;
  15556. };
  15557. Hls.prototype.attachMedia = function attachMedia(media) {
  15558. logger["b" /* logger */].log('attachMedia');
  15559. this.media = media;
  15560. this.trigger(events["a" /* default */].MEDIA_ATTACHING, {media: media});
  15561. };
  15562. Hls.prototype.detachMedia = function detachMedia() {
  15563. logger["b" /* logger */].log('detachMedia');
  15564. this.trigger(events["a" /* default */].MEDIA_DETACHING);
  15565. this.media = null;
  15566. };
  15567. Hls.prototype.loadSource = function loadSource(url) {
  15568. url = url_toolkit_default.a.buildAbsoluteURL(window.location.href, url, {alwaysNormalize: true});
  15569. logger["b" /* logger */].log('loadSource:' + url);
  15570. this.url = url;
  15571. // when attaching to a source URL, trigger a playlist load
  15572. this.trigger(events["a" /* default */].MANIFEST_LOADING, {url: url});
  15573. };
  15574. Hls.prototype.startLoad = function startLoad() {
  15575. var startPosition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1;
  15576. logger["b" /* logger */].log('startLoad(' + startPosition + ')');
  15577. this.networkControllers.forEach(function (controller) {
  15578. controller.startLoad(startPosition);
  15579. });
  15580. };
  15581. Hls.prototype.stopLoad = function stopLoad() {
  15582. logger["b" /* logger */].log('stopLoad');
  15583. this.networkControllers.forEach(function (controller) {
  15584. controller.stopLoad();
  15585. });
  15586. };
  15587. Hls.prototype.swapAudioCodec = function swapAudioCodec() {
  15588. logger["b" /* logger */].log('swapAudioCodec');
  15589. this.streamController.swapAudioCodec();
  15590. };
  15591. Hls.prototype.recoverMediaError = function recoverMediaError() {
  15592. logger["b" /* logger */].log('recoverMediaError');
  15593. var media = this.media;
  15594. this.detachMedia();
  15595. this.attachMedia(media);
  15596. };
  15597. /** Return all quality levels **/
  15598. hls__createClass(Hls, [{
  15599. key: 'levels',
  15600. get: function get() {
  15601. return this.levelController.levels;
  15602. }
  15603. /** Return current playback quality level **/
  15604. }, {
  15605. key: 'currentLevel',
  15606. get: function get() {
  15607. return this.streamController.currentLevel;
  15608. }
  15609. /* set quality level immediately (-1 for automatic level selection) */
  15610. ,
  15611. set: function set(newLevel) {
  15612. logger["b" /* logger */].log('set currentLevel:' + newLevel);
  15613. this.loadLevel = newLevel;
  15614. this.streamController.immediateLevelSwitch();
  15615. }
  15616. /** Return next playback quality level (quality level of next fragment) **/
  15617. }, {
  15618. key: 'nextLevel',
  15619. get: function get() {
  15620. return this.streamController.nextLevel;
  15621. }
  15622. /* set quality level for next fragment (-1 for automatic level selection) */
  15623. ,
  15624. set: function set(newLevel) {
  15625. logger["b" /* logger */].log('set nextLevel:' + newLevel);
  15626. this.levelController.manualLevel = newLevel;
  15627. this.streamController.nextLevelSwitch();
  15628. }
  15629. /** Return the quality level of current/last loaded fragment **/
  15630. }, {
  15631. key: 'loadLevel',
  15632. get: function get() {
  15633. return this.levelController.level;
  15634. }
  15635. /* set quality level for current/next loaded fragment (-1 for automatic level selection) */
  15636. ,
  15637. set: function set(newLevel) {
  15638. logger["b" /* logger */].log('set loadLevel:' + newLevel);
  15639. this.levelController.manualLevel = newLevel;
  15640. }
  15641. /** Return the quality level of next loaded fragment **/
  15642. }, {
  15643. key: 'nextLoadLevel',
  15644. get: function get() {
  15645. return this.levelController.nextLoadLevel;
  15646. }
  15647. /** set quality level of next loaded fragment **/
  15648. ,
  15649. set: function set(level) {
  15650. this.levelController.nextLoadLevel = level;
  15651. }
  15652. /** Return first level (index of first level referenced in manifest)
  15653. **/
  15654. }, {
  15655. key: 'firstLevel',
  15656. get: function get() {
  15657. return Math.max(this.levelController.firstLevel, this.minAutoLevel);
  15658. }
  15659. /** set first level (index of first level referenced in manifest)
  15660. **/
  15661. ,
  15662. set: function set(newLevel) {
  15663. logger["b" /* logger */].log('set firstLevel:' + newLevel);
  15664. this.levelController.firstLevel = newLevel;
  15665. }
  15666. /** Return start level (level of first fragment that will be played back)
  15667. if not overrided by user, first level appearing in manifest will be used as start level
  15668. if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
  15669. **/
  15670. }, {
  15671. key: 'startLevel',
  15672. get: function get() {
  15673. return this.levelController.startLevel;
  15674. }
  15675. /** set start level (level of first fragment that will be played back)
  15676. if not overrided by user, first level appearing in manifest will be used as start level
  15677. if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
  15678. **/
  15679. ,
  15680. set: function set(newLevel) {
  15681. logger["b" /* logger */].log('set startLevel:' + newLevel);
  15682. var hls = this;
  15683. // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
  15684. if (newLevel !== -1) {
  15685. newLevel = Math.max(newLevel, hls.minAutoLevel);
  15686. }
  15687. hls.levelController.startLevel = newLevel;
  15688. }
  15689. /** Return the capping/max level value that could be used by automatic level selection algorithm **/
  15690. }, {
  15691. key: 'autoLevelCapping',
  15692. get: function get() {
  15693. return this._autoLevelCapping;
  15694. }
  15695. /** set the capping/max level value that could be used by automatic level selection algorithm **/
  15696. ,
  15697. set: function set(newLevel) {
  15698. logger["b" /* logger */].log('set autoLevelCapping:' + newLevel);
  15699. this._autoLevelCapping = newLevel;
  15700. }
  15701. /* check if we are in automatic level selection mode */
  15702. }, {
  15703. key: 'autoLevelEnabled',
  15704. get: function get() {
  15705. return this.levelController.manualLevel === -1;
  15706. }
  15707. /* return manual level */
  15708. }, {
  15709. key: 'manualLevel',
  15710. get: function get() {
  15711. return this.levelController.manualLevel;
  15712. }
  15713. /* return min level selectable in auto mode according to config.minAutoBitrate */
  15714. }, {
  15715. key: 'minAutoLevel',
  15716. get: function get() {
  15717. var hls = this,
  15718. levels = hls.levels,
  15719. minAutoBitrate = hls.config.minAutoBitrate,
  15720. len = levels ? levels.length : 0;
  15721. for (var i = 0; i < len; i++) {
  15722. var levelNextBitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate;
  15723. if (levelNextBitrate > minAutoBitrate) {
  15724. return i;
  15725. }
  15726. }
  15727. return 0;
  15728. }
  15729. /* return max level selectable in auto mode according to autoLevelCapping */
  15730. }, {
  15731. key: 'maxAutoLevel',
  15732. get: function get() {
  15733. var hls = this;
  15734. var levels = hls.levels;
  15735. var autoLevelCapping = hls.autoLevelCapping;
  15736. var maxAutoLevel = void 0;
  15737. if (autoLevelCapping === -1 && levels && levels.length) {
  15738. maxAutoLevel = levels.length - 1;
  15739. } else {
  15740. maxAutoLevel = autoLevelCapping;
  15741. }
  15742. return maxAutoLevel;
  15743. }
  15744. // return next auto level
  15745. }, {
  15746. key: 'nextAutoLevel',
  15747. get: function get() {
  15748. var hls = this;
  15749. // ensure next auto level is between min and max auto level
  15750. return Math.min(Math.max(hls.abrController.nextAutoLevel, hls.minAutoLevel), hls.maxAutoLevel);
  15751. }
  15752. // this setter is used to force next auto level
  15753. // this is useful to force a switch down in auto mode : in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example)
  15754. // forced value is valid for one fragment. upon succesful frag loading at forced level, this value will be resetted to -1 by ABR controller
  15755. ,
  15756. set: function set(nextLevel) {
  15757. var hls = this;
  15758. hls.abrController.nextAutoLevel = Math.max(hls.minAutoLevel, nextLevel);
  15759. }
  15760. /** get alternate audio tracks list from playlist **/
  15761. }, {
  15762. key: 'audioTracks',
  15763. get: function get() {
  15764. var audioTrackController = this.audioTrackController;
  15765. return audioTrackController ? audioTrackController.audioTracks : [];
  15766. }
  15767. /** get index of the selected audio track (index in audio track lists) **/
  15768. }, {
  15769. key: 'audioTrack',
  15770. get: function get() {
  15771. var audioTrackController = this.audioTrackController;
  15772. return audioTrackController ? audioTrackController.audioTrack : -1;
  15773. }
  15774. /** select an audio track, based on its index in audio track lists**/
  15775. ,
  15776. set: function set(audioTrackId) {
  15777. var audioTrackController = this.audioTrackController;
  15778. if (audioTrackController) {
  15779. audioTrackController.audioTrack = audioTrackId;
  15780. }
  15781. }
  15782. }, {
  15783. key: 'liveSyncPosition',
  15784. get: function get() {
  15785. return this.streamController.liveSyncPosition;
  15786. }
  15787. /** get alternate subtitle tracks list from playlist **/
  15788. }, {
  15789. key: 'subtitleTracks',
  15790. get: function get() {
  15791. var subtitleTrackController = this.subtitleTrackController;
  15792. return subtitleTrackController ? subtitleTrackController.subtitleTracks : [];
  15793. }
  15794. /** get index of the selected subtitle track (index in subtitle track lists) **/
  15795. }, {
  15796. key: 'subtitleTrack',
  15797. get: function get() {
  15798. var subtitleTrackController = this.subtitleTrackController;
  15799. return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;
  15800. }
  15801. /** select an subtitle track, based on its index in subtitle track lists**/
  15802. ,
  15803. set: function set(subtitleTrackId) {
  15804. var subtitleTrackController = this.subtitleTrackController;
  15805. if (subtitleTrackController) {
  15806. subtitleTrackController.subtitleTrack = subtitleTrackId;
  15807. }
  15808. }
  15809. }, {
  15810. key: 'subtitleDisplay',
  15811. get: function get() {
  15812. var subtitleTrackController = this.subtitleTrackController;
  15813. return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false;
  15814. },
  15815. set: function set(value) {
  15816. var subtitleTrackController = this.subtitleTrackController;
  15817. if (subtitleTrackController) {
  15818. subtitleTrackController.subtitleDisplay = value;
  15819. }
  15820. }
  15821. }]);
  15822. return Hls;
  15823. }();
  15824. /* harmony default export */
  15825. var src_hls = __webpack_exports__["default"] = (hls_Hls);
  15826. /***/
  15827. }),
  15828. /* 10 */
  15829. /***/ (function (module, exports, __webpack_require__) {
  15830. function webpackBootstrapFunc(modules) {
  15831. /******/ // The module cache
  15832. /******/
  15833. var installedModules = {};
  15834. /******/ // The require function
  15835. /******/
  15836. function __webpack_require__(moduleId) {
  15837. /******/ // Check if module is in cache
  15838. /******/
  15839. if (installedModules[moduleId])
  15840. /******/ return installedModules[moduleId].exports;
  15841. /******/ // Create a new module (and put it into the cache)
  15842. /******/
  15843. var module = installedModules[moduleId] = {
  15844. /******/ i: moduleId,
  15845. /******/ l: false,
  15846. /******/ exports: {}
  15847. /******/
  15848. };
  15849. /******/ // Execute the module function
  15850. /******/
  15851. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  15852. /******/ // Flag the module as loaded
  15853. /******/
  15854. module.l = true;
  15855. /******/ // Return the exports of the module
  15856. /******/
  15857. return module.exports;
  15858. /******/
  15859. }
  15860. /******/ // expose the modules object (__webpack_modules__)
  15861. /******/
  15862. __webpack_require__.m = modules;
  15863. /******/ // expose the module cache
  15864. /******/
  15865. __webpack_require__.c = installedModules;
  15866. /******/ // identity function for calling harmony imports with the correct context
  15867. /******/
  15868. __webpack_require__.i = function (value) {
  15869. return value;
  15870. };
  15871. /******/ // define getter function for harmony exports
  15872. /******/
  15873. __webpack_require__.d = function (exports, name, getter) {
  15874. /******/
  15875. if (!__webpack_require__.o(exports, name)) {
  15876. /******/
  15877. Object.defineProperty(exports, name, {
  15878. /******/ configurable: false,
  15879. /******/ enumerable: true,
  15880. /******/ get: getter
  15881. /******/
  15882. });
  15883. /******/
  15884. }
  15885. /******/
  15886. };
  15887. /******/ // getDefaultExport function for compatibility with non-harmony modules
  15888. /******/
  15889. __webpack_require__.n = function (module) {
  15890. /******/
  15891. var getter = module && module.__esModule ?
  15892. /******/ function getDefault() {
  15893. return module['default'];
  15894. } :
  15895. /******/ function getModuleExports() {
  15896. return module;
  15897. };
  15898. /******/
  15899. __webpack_require__.d(getter, 'a', getter);
  15900. /******/
  15901. return getter;
  15902. /******/
  15903. };
  15904. /******/ // Object.prototype.hasOwnProperty.call
  15905. /******/
  15906. __webpack_require__.o = function (object, property) {
  15907. return Object.prototype.hasOwnProperty.call(object, property);
  15908. };
  15909. /******/ // __webpack_public_path__
  15910. /******/
  15911. __webpack_require__.p = "/";
  15912. /******/ // on error function for async loading
  15913. /******/
  15914. __webpack_require__.oe = function (err) {
  15915. console.error(err);
  15916. throw err;
  15917. };
  15918. var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE)
  15919. return f.default || f // try to call default if defined to also support babel esmodule exports
  15920. }
  15921. var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+'
  15922. var dependencyRegExp = '\\((\/\\*.*?\\*\/)?\s?.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true
  15923. // http://stackoverflow.com/a/2593661/130442
  15924. function quoteRegExp(str) {
  15925. return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&')
  15926. }
  15927. function getModuleDependencies(sources, module, queueName) {
  15928. var retval = {}
  15929. retval[queueName] = []
  15930. var fnString = module.toString()
  15931. var wrapperSignature = fnString.match(/^function\s?\(\w+,\s*\w+,\s*(\w+)\)/)
  15932. if (!wrapperSignature) return retval
  15933. var webpackRequireName = wrapperSignature[1]
  15934. // main bundle deps
  15935. var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g')
  15936. var match
  15937. while ((match = re.exec(fnString))) {
  15938. if (match[3] === 'dll-reference') continue
  15939. retval[queueName].push(match[3])
  15940. }
  15941. // dll deps
  15942. re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g')
  15943. while ((match = re.exec(fnString))) {
  15944. if (!sources[match[2]]) {
  15945. retval[queueName].push(match[1])
  15946. sources[match[2]] = __webpack_require__(match[1]).m
  15947. }
  15948. retval[match[2]] = retval[match[2]] || []
  15949. retval[match[2]].push(match[4])
  15950. }
  15951. return retval
  15952. }
  15953. function hasValuesInQueues(queues) {
  15954. var keys = Object.keys(queues)
  15955. return keys.reduce(function (hasValues, key) {
  15956. return hasValues || queues[key].length > 0
  15957. }, false)
  15958. }
  15959. function getRequiredModules(sources, moduleId) {
  15960. var modulesQueue = {
  15961. main: [moduleId]
  15962. }
  15963. var requiredModules = {
  15964. main: []
  15965. }
  15966. var seenModules = {
  15967. main: {}
  15968. }
  15969. while (hasValuesInQueues(modulesQueue)) {
  15970. var queues = Object.keys(modulesQueue)
  15971. for (var i = 0; i < queues.length; i++) {
  15972. var queueName = queues[i]
  15973. var queue = modulesQueue[queueName]
  15974. var moduleToCheck = queue.pop()
  15975. seenModules[queueName] = seenModules[queueName] || {}
  15976. if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue
  15977. seenModules[queueName][moduleToCheck] = true
  15978. requiredModules[queueName] = requiredModules[queueName] || []
  15979. requiredModules[queueName].push(moduleToCheck)
  15980. var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName)
  15981. var newModulesKeys = Object.keys(newModules)
  15982. for (var j = 0; j < newModulesKeys.length; j++) {
  15983. modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || []
  15984. modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]])
  15985. }
  15986. }
  15987. }
  15988. return requiredModules
  15989. }
  15990. module.exports = function (moduleId, options) {
  15991. options = options || {}
  15992. var sources = {
  15993. main: __webpack_require__.m
  15994. }
  15995. var requiredModules = options.all ? {main: Object.keys(sources)} : getRequiredModules(sources, moduleId)
  15996. var src = ''
  15997. Object.keys(requiredModules).filter(function (m) {
  15998. return m !== 'main'
  15999. }).forEach(function (module) {
  16000. var entryModule = 0
  16001. while (requiredModules[module][entryModule]) {
  16002. entryModule++
  16003. }
  16004. requiredModules[module].push(entryModule)
  16005. sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })'
  16006. src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) {
  16007. return '' + JSON.stringify(id) + ': ' + sources[module][id].toString()
  16008. }).join(',') + '});\n'
  16009. })
  16010. src = src + '(' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) {
  16011. return '' + JSON.stringify(id) + ': ' + sources.main[id].toString()
  16012. }).join(',') + '})(self);'
  16013. var blob = new window.Blob([src], {type: 'text/javascript'})
  16014. if (options.bare) {
  16015. return blob
  16016. }
  16017. var URL = window.URL || window.webkitURL || window.mozURL || window.msURL
  16018. var workerUrl = URL.createObjectURL(blob)
  16019. var worker = new window.Worker(workerUrl)
  16020. worker.objectURL = workerUrl
  16021. return worker
  16022. }
  16023. /***/
  16024. }),
  16025. /* 11 */
  16026. /***/ (function (module, __webpack_exports__, __webpack_require__) {
  16027. "use strict";
  16028. Object.defineProperty(__webpack_exports__, "__esModule", {value: true});
  16029. /* harmony import */
  16030. var __WEBPACK_IMPORTED_MODULE_0__demux_demuxer_inline__ = __webpack_require__(8);
  16031. /* harmony import */
  16032. var __WEBPACK_IMPORTED_MODULE_1__events__ = __webpack_require__(1);
  16033. /* harmony import */
  16034. var __WEBPACK_IMPORTED_MODULE_2__utils_logger__ = __webpack_require__(0);
  16035. /* harmony import */
  16036. var __WEBPACK_IMPORTED_MODULE_3_events__ = __webpack_require__(6);
  16037. /* harmony import */
  16038. var __WEBPACK_IMPORTED_MODULE_3_events___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3_events__);
  16039. /* demuxer web worker.
  16040. * - listen to worker message, and trigger DemuxerInline upon reception of Fragments.
  16041. * - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead.
  16042. */
  16043. var DemuxerWorker = function DemuxerWorker(self) {
  16044. // observer setup
  16045. var observer = new __WEBPACK_IMPORTED_MODULE_3_events___default.a();
  16046. observer.trigger = function trigger(event) {
  16047. for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  16048. data[_key - 1] = arguments[_key];
  16049. }
  16050. observer.emit.apply(observer, [event, event].concat(data));
  16051. };
  16052. observer.off = function off(event) {
  16053. for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  16054. data[_key2 - 1] = arguments[_key2];
  16055. }
  16056. observer.removeListener.apply(observer, [event].concat(data));
  16057. };
  16058. var forwardMessage = function forwardMessage(ev, data) {
  16059. self.postMessage({event: ev, data: data});
  16060. };
  16061. self.addEventListener('message', function (ev) {
  16062. var data = ev.data;
  16063. //console.log('demuxer cmd:' + data.cmd);
  16064. switch (data.cmd) {
  16065. case 'init':
  16066. var config = JSON.parse(data.config);
  16067. self.demuxer = new __WEBPACK_IMPORTED_MODULE_0__demux_demuxer_inline__["a" /* default */](observer, data.typeSupported, config, data.vendor);
  16068. try {
  16069. Object(__WEBPACK_IMPORTED_MODULE_2__utils_logger__["a" /* enableLogs */])(config.debug === true);
  16070. } catch (err) {
  16071. console.warn('demuxerWorker: unable to enable logs');
  16072. }
  16073. // signal end of worker init
  16074. forwardMessage('init', null);
  16075. break;
  16076. case 'demux':
  16077. self.demuxer.push(data.data, data.decryptdata, data.initSegment, data.audioCodec, data.videoCodec, data.timeOffset, data.discontinuity, data.trackSwitch, data.contiguous, data.duration, data.accurateTimeOffset, data.defaultInitPTS);
  16078. break;
  16079. default:
  16080. break;
  16081. }
  16082. });
  16083. // forward events to main thread
  16084. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_DECRYPTED, forwardMessage);
  16085. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_INIT_SEGMENT, forwardMessage);
  16086. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSED, forwardMessage);
  16087. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].ERROR, forwardMessage);
  16088. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_METADATA, forwardMessage);
  16089. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_USERDATA, forwardMessage);
  16090. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].INIT_PTS_FOUND, forwardMessage);
  16091. // special case for FRAG_PARSING_DATA: pass data1/data2 as transferable object (no copy)
  16092. observer.on(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_DATA, function (ev, data) {
  16093. var transferable = [];
  16094. var message = {event: ev, data: data};
  16095. if (data.data1) {
  16096. message.data1 = data.data1.buffer;
  16097. transferable.push(data.data1.buffer);
  16098. delete data.data1;
  16099. }
  16100. if (data.data2) {
  16101. message.data2 = data.data2.buffer;
  16102. transferable.push(data.data2.buffer);
  16103. delete data.data2;
  16104. }
  16105. self.postMessage(message, transferable);
  16106. });
  16107. };
  16108. /* harmony default export */
  16109. __webpack_exports__["default"] = (DemuxerWorker);
  16110. /***/
  16111. }),
  16112. /* 12 */
  16113. /***/ (function (module, exports) {
  16114. /*! http://mths.be/endswith v0.2.0 by @mathias */
  16115. if (!String.prototype.endsWith) {
  16116. (function () {
  16117. 'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
  16118. var defineProperty = (function () {
  16119. // IE 8 only supports `Object.defineProperty` on DOM elements
  16120. try {
  16121. var object = {};
  16122. var $defineProperty = Object.defineProperty;
  16123. var result = $defineProperty(object, object, object) && $defineProperty;
  16124. } catch (error) {
  16125. }
  16126. return result;
  16127. }());
  16128. var toString = {}.toString;
  16129. var endsWith = function (search) {
  16130. if (this == null) {
  16131. throw TypeError();
  16132. }
  16133. var string = String(this);
  16134. if (search && toString.call(search) == '[object RegExp]') {
  16135. throw TypeError();
  16136. }
  16137. var stringLength = string.length;
  16138. var searchString = String(search);
  16139. var searchLength = searchString.length;
  16140. var pos = stringLength;
  16141. if (arguments.length > 1) {
  16142. var position = arguments[1];
  16143. if (position !== undefined) {
  16144. // `ToInteger`
  16145. pos = position ? Number(position) : 0;
  16146. if (pos != pos) { // better `isNaN`
  16147. pos = 0;
  16148. }
  16149. }
  16150. }
  16151. var end = Math.min(Math.max(pos, 0), stringLength);
  16152. var start = end - searchLength;
  16153. if (start < 0) {
  16154. return false;
  16155. }
  16156. var index = -1;
  16157. while (++index < searchLength) {
  16158. if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {
  16159. return false;
  16160. }
  16161. }
  16162. return true;
  16163. };
  16164. if (defineProperty) {
  16165. defineProperty(String.prototype, 'endsWith', {
  16166. 'value': endsWith,
  16167. 'configurable': true,
  16168. 'writable': true
  16169. });
  16170. } else {
  16171. String.prototype.endsWith = endsWith;
  16172. }
  16173. }());
  16174. }
  16175. /***/
  16176. })
  16177. /******/])["default"];
  16178. });
  16179. //# sourceMappingURL=hls.js.map