videojs-contrib-media-sources.js 288 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266
  1. /**
  2. * videojs-contrib-media-sources
  3. * @version 4.7.2
  4. * @copyright 2018 Brightcove, Inc.
  5. * @license Apache-2.0
  6. */
  7. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojsContribMediaSources = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  8. (function (global){
  9. /**
  10. * @file add-text-track-data.js
  11. */
  12. 'use strict';
  13. Object.defineProperty(exports, '__esModule', {
  14. value: true
  15. });
  16. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17. var _globalWindow = require('global/window');
  18. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  19. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  20. var _videoJs2 = _interopRequireDefault(_videoJs);
  21. /**
  22. * Define properties on a cue for backwards compatability,
  23. * but warn the user that the way that they are using it
  24. * is depricated and will be removed at a later date.
  25. *
  26. * @param {Cue} cue the cue to add the properties on
  27. * @private
  28. */
  29. var deprecateOldCue = function deprecateOldCue(cue) {
  30. Object.defineProperties(cue.frame, {
  31. id: {
  32. get: function get() {
  33. _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
  34. return cue.value.key;
  35. }
  36. },
  37. value: {
  38. get: function get() {
  39. _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
  40. return cue.value.data;
  41. }
  42. },
  43. privateData: {
  44. get: function get() {
  45. _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
  46. return cue.value.data;
  47. }
  48. }
  49. });
  50. };
  51. var durationOfVideo = function durationOfVideo(duration) {
  52. var dur = undefined;
  53. if (isNaN(duration) || Math.abs(duration) === Infinity) {
  54. dur = Number.MAX_VALUE;
  55. } else {
  56. dur = duration;
  57. }
  58. return dur;
  59. };
  60. /**
  61. * Add text track data to a source handler given the captions and
  62. * metadata from the buffer.
  63. *
  64. * @param {Object} sourceHandler the flash or virtual source buffer
  65. * @param {Array} captionArray an array of caption data
  66. * @param {Array} metadataArray an array of meta data
  67. * @private
  68. */
  69. var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
  70. var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
  71. if (captionArray) {
  72. captionArray.forEach(function (caption) {
  73. var track = caption.stream;
  74. this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
  75. }, sourceHandler);
  76. }
  77. if (metadataArray) {
  78. (function () {
  79. var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
  80. metadataArray.forEach(function (metadata) {
  81. var time = metadata.cueTime + this.timestampOffset;
  82. metadata.frames.forEach(function (frame) {
  83. var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
  84. cue.frame = frame;
  85. cue.value = frame;
  86. deprecateOldCue(cue);
  87. this.metadataTrack_.addCue(cue);
  88. }, this);
  89. }, sourceHandler);
  90. // Updating the metadeta cues so that
  91. // the endTime of each cue is the startTime of the next cue
  92. // the endTime of last cue is the duration of the video
  93. if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
  94. (function () {
  95. var cues = sourceHandler.metadataTrack_.cues;
  96. var cuesArray = [];
  97. // Create a copy of the TextTrackCueList...
  98. // ...disregarding cues with a falsey value
  99. for (var i = 0; i < cues.length; i++) {
  100. if (cues[i]) {
  101. cuesArray.push(cues[i]);
  102. }
  103. }
  104. // Group cues by their startTime value
  105. var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
  106. var timeSlot = obj[cue.startTime] || [];
  107. timeSlot.push(cue);
  108. obj[cue.startTime] = timeSlot;
  109. return obj;
  110. }, {});
  111. // Sort startTimes by ascending order
  112. var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
  113. return Number(a) - Number(b);
  114. });
  115. // Map each cue group's endTime to the next group's startTime
  116. sortedStartTimes.forEach(function (startTime, idx) {
  117. var cueGroup = cuesGroupedByStartTime[startTime];
  118. var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
  119. // Map each cue's endTime the next group's startTime
  120. cueGroup.forEach(function (cue) {
  121. cue.endTime = nextTime;
  122. });
  123. });
  124. })();
  125. }
  126. })();
  127. }
  128. };
  129. exports['default'] = {
  130. addTextTrackData: addTextTrackData,
  131. durationOfVideo: durationOfVideo
  132. };
  133. module.exports = exports['default'];
  134. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  135. },{"global/window":14}],2:[function(require,module,exports){
  136. /**
  137. * @file codec-utils.js
  138. */
  139. /**
  140. * Check if a codec string refers to an audio codec.
  141. *
  142. * @param {String} codec codec string to check
  143. * @return {Boolean} if this is an audio codec
  144. * @private
  145. */
  146. 'use strict';
  147. Object.defineProperty(exports, '__esModule', {
  148. value: true
  149. });
  150. var isAudioCodec = function isAudioCodec(codec) {
  151. return (/mp4a\.\d+.\d+/i.test(codec)
  152. );
  153. };
  154. /**
  155. * Check if a codec string refers to a video codec.
  156. *
  157. * @param {String} codec codec string to check
  158. * @return {Boolean} if this is a video codec
  159. * @private
  160. */
  161. var isVideoCodec = function isVideoCodec(codec) {
  162. return (/avc1\.[\da-f]+/i.test(codec)
  163. );
  164. };
  165. /**
  166. * Parse a content type header into a type and parameters
  167. * object
  168. *
  169. * @param {String} type the content type header
  170. * @return {Object} the parsed content-type
  171. * @private
  172. */
  173. var parseContentType = function parseContentType(type) {
  174. var object = { type: '', parameters: {} };
  175. var parameters = type.trim().split(';');
  176. // first parameter should always be content-type
  177. object.type = parameters.shift().trim();
  178. parameters.forEach(function (parameter) {
  179. var pair = parameter.trim().split('=');
  180. if (pair.length > 1) {
  181. var _name = pair[0].replace(/"/g, '').trim();
  182. var value = pair[1].replace(/"/g, '').trim();
  183. object.parameters[_name] = value;
  184. }
  185. });
  186. return object;
  187. };
  188. /**
  189. * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
  190. * `avc1.<hhhhhh>`
  191. *
  192. * @param {Array} codecs an array of codec strings to fix
  193. * @return {Array} the translated codec array
  194. * @private
  195. */
  196. var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  197. return codecs.map(function (codec) {
  198. return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
  199. var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
  200. var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
  201. return 'avc1.' + profileHex + '00' + avcLevelHex;
  202. });
  203. });
  204. };
  205. exports['default'] = {
  206. isAudioCodec: isAudioCodec,
  207. parseContentType: parseContentType,
  208. isVideoCodec: isVideoCodec,
  209. translateLegacyCodecs: translateLegacyCodecs
  210. };
  211. module.exports = exports['default'];
  212. },{}],3:[function(require,module,exports){
  213. /**
  214. * @file create-text-tracks-if-necessary.js
  215. */
  216. /**
  217. * Create text tracks on video.js if they exist on a segment.
  218. *
  219. * @param {Object} sourceBuffer the VSB or FSB
  220. * @param {Object} mediaSource the HTML or Flash media source
  221. * @param {Object} segment the segment that may contain the text track
  222. * @private
  223. */
  224. 'use strict';
  225. Object.defineProperty(exports, '__esModule', {
  226. value: true
  227. });
  228. var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
  229. var player = mediaSource.player_;
  230. // create an in-band caption track if one is present in the segment
  231. if (segment.captions && segment.captions.length) {
  232. if (!sourceBuffer.inbandTextTracks_) {
  233. sourceBuffer.inbandTextTracks_ = {};
  234. }
  235. for (var trackId in segment.captionStreams) {
  236. if (!sourceBuffer.inbandTextTracks_[trackId]) {
  237. player.tech_.trigger({ type: 'usage', name: 'hls-608' });
  238. var track = player.textTracks().getTrackById(trackId);
  239. if (track) {
  240. // Resuse an existing track with a CC# id because this was
  241. // very likely created by videojs-contrib-hls from information
  242. // in the m3u8 for us to use
  243. sourceBuffer.inbandTextTracks_[trackId] = track;
  244. } else {
  245. // Otherwise, create a track with the default `CC#` label and
  246. // without a language
  247. sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
  248. kind: 'captions',
  249. id: trackId,
  250. label: trackId
  251. }, false).track;
  252. }
  253. }
  254. }
  255. }
  256. if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
  257. sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
  258. kind: 'metadata',
  259. label: 'Timed Metadata'
  260. }, false).track;
  261. sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
  262. }
  263. };
  264. exports['default'] = createTextTracksIfNecessary;
  265. module.exports = exports['default'];
  266. },{}],4:[function(require,module,exports){
  267. /**
  268. * @file flash-constants.js
  269. */
  270. /**
  271. * The maximum size in bytes for append operations to the video.js
  272. * SWF. Calling through to Flash blocks and can be expensive so
  273. * we chunk data and pass through 4KB at a time, yielding to the
  274. * browser between chunks. This gives a theoretical maximum rate of
  275. * 1MB/s into Flash. Any higher and we begin to drop frames and UI
  276. * responsiveness suffers.
  277. *
  278. * @private
  279. */
  280. "use strict";
  281. Object.defineProperty(exports, "__esModule", {
  282. value: true
  283. });
  284. var flashConstants = {
  285. // times in milliseconds
  286. TIME_BETWEEN_CHUNKS: 1,
  287. BYTES_PER_CHUNK: 1024 * 32
  288. };
  289. exports["default"] = flashConstants;
  290. module.exports = exports["default"];
  291. },{}],5:[function(require,module,exports){
  292. (function (global){
  293. /**
  294. * @file flash-media-source.js
  295. */
  296. 'use strict';
  297. Object.defineProperty(exports, '__esModule', {
  298. value: true
  299. });
  300. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  301. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  302. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  303. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  304. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  305. var _globalDocument = require('global/document');
  306. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  307. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  308. var _videoJs2 = _interopRequireDefault(_videoJs);
  309. var _flashSourceBuffer = require('./flash-source-buffer');
  310. var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
  311. var _flashConstants = require('./flash-constants');
  312. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  313. var _codecUtils = require('./codec-utils');
  314. /**
  315. * A flash implmentation of HTML MediaSources and a polyfill
  316. * for browsers that don't support native or HTML MediaSources..
  317. *
  318. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  319. * @class FlashMediaSource
  320. * @extends videojs.EventTarget
  321. */
  322. var FlashMediaSource = (function (_videojs$EventTarget) {
  323. _inherits(FlashMediaSource, _videojs$EventTarget);
  324. function FlashMediaSource() {
  325. var _this = this;
  326. _classCallCheck(this, FlashMediaSource);
  327. _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
  328. this.sourceBuffers = [];
  329. this.readyState = 'closed';
  330. this.on(['sourceopen', 'webkitsourceopen'], function (event) {
  331. // find the swf where we will push media data
  332. _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
  333. _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
  334. _this.tech_ = _this.swfObj.tech;
  335. _this.readyState = 'open';
  336. _this.tech_.on('seeking', function () {
  337. var i = _this.sourceBuffers.length;
  338. while (i--) {
  339. _this.sourceBuffers[i].abort();
  340. }
  341. });
  342. // trigger load events
  343. if (_this.swfObj) {
  344. _this.swfObj.vjs_load();
  345. }
  346. });
  347. }
  348. /**
  349. * Set or return the presentation duration.
  350. *
  351. * @param {Double} value the duration of the media in seconds
  352. * @param {Double} the current presentation duration
  353. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  354. */
  355. /**
  356. * We have this function so that the html and flash interfaces
  357. * are the same.
  358. *
  359. * @private
  360. */
  361. _createClass(FlashMediaSource, [{
  362. key: 'addSeekableRange_',
  363. value: function addSeekableRange_() {}
  364. // intentional no-op
  365. /**
  366. * Create a new flash source buffer and add it to our flash media source.
  367. *
  368. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  369. * @param {String} type the content-type of the source
  370. * @return {Object} the flash source buffer
  371. */
  372. }, {
  373. key: 'addSourceBuffer',
  374. value: function addSourceBuffer(type) {
  375. var parsedType = (0, _codecUtils.parseContentType)(type);
  376. var sourceBuffer = undefined;
  377. // if this is an FLV type, we'll push data to flash
  378. if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') {
  379. // Flash source buffers
  380. sourceBuffer = new _flashSourceBuffer2['default'](this);
  381. } else {
  382. throw new Error('NotSupportedError (Video.js)');
  383. }
  384. this.sourceBuffers.push(sourceBuffer);
  385. return sourceBuffer;
  386. }
  387. /**
  388. * Signals the end of the stream.
  389. *
  390. * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
  391. * @param {String=} error Signals that a playback error
  392. * has occurred. If specified, it must be either "network" or
  393. * "decode".
  394. */
  395. }, {
  396. key: 'endOfStream',
  397. value: function endOfStream(error) {
  398. if (error === 'network') {
  399. // MEDIA_ERR_NETWORK
  400. this.tech_.error(2);
  401. } else if (error === 'decode') {
  402. // MEDIA_ERR_DECODE
  403. this.tech_.error(3);
  404. }
  405. if (this.readyState !== 'ended') {
  406. this.readyState = 'ended';
  407. this.swfObj.vjs_endOfStream();
  408. }
  409. }
  410. }]);
  411. return FlashMediaSource;
  412. })(_videoJs2['default'].EventTarget);
  413. exports['default'] = FlashMediaSource;
  414. try {
  415. Object.defineProperty(FlashMediaSource.prototype, 'duration', {
  416. /**
  417. * Return the presentation duration.
  418. *
  419. * @return {Double} the duration of the media in seconds
  420. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  421. */
  422. get: function get() {
  423. if (!this.swfObj) {
  424. return NaN;
  425. }
  426. // get the current duration from the SWF
  427. return this.swfObj.vjs_getProperty('duration');
  428. },
  429. /**
  430. * Set the presentation duration.
  431. *
  432. * @param {Double} value the duration of the media in seconds
  433. * @return {Double} the duration of the media in seconds
  434. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  435. */
  436. set: function set(value) {
  437. var i = undefined;
  438. var oldDuration = this.swfObj.vjs_getProperty('duration');
  439. this.swfObj.vjs_setProperty('duration', value);
  440. if (value < oldDuration) {
  441. // In MSE, this triggers the range removal algorithm which causes
  442. // an update to occur
  443. for (i = 0; i < this.sourceBuffers.length; i++) {
  444. this.sourceBuffers[i].remove(value, oldDuration);
  445. }
  446. }
  447. return value;
  448. }
  449. });
  450. } catch (e) {
  451. // IE8 throws if defineProperty is called on a non-DOM node. We
  452. // don't support IE8 but we shouldn't throw an error if loaded
  453. // there.
  454. FlashMediaSource.prototype.duration = NaN;
  455. }
  456. for (var property in _flashConstants2['default']) {
  457. FlashMediaSource[property] = _flashConstants2['default'][property];
  458. }
  459. module.exports = exports['default'];
  460. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  461. },{"./codec-utils":2,"./flash-constants":4,"./flash-source-buffer":6,"global/document":13}],6:[function(require,module,exports){
  462. (function (global){
  463. /**
  464. * @file flash-source-buffer.js
  465. */
  466. 'use strict';
  467. Object.defineProperty(exports, '__esModule', {
  468. value: true
  469. });
  470. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  471. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  472. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  473. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  474. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  475. var _globalWindow = require('global/window');
  476. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  477. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  478. var _videoJs2 = _interopRequireDefault(_videoJs);
  479. var _muxJsLibFlv = require('mux.js/lib/flv');
  480. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  481. var _removeCuesFromTrack = require('./remove-cues-from-track');
  482. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  483. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  484. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  485. var _addTextTrackData = require('./add-text-track-data');
  486. var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
  487. var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
  488. var _webwackify = require('webwackify');
  489. var _webwackify2 = _interopRequireDefault(_webwackify);
  490. var _flashConstants = require('./flash-constants');
  491. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  492. var resolveFlashTransmuxWorker = function resolveFlashTransmuxWorker() {
  493. var result = undefined;
  494. try {
  495. result = require.resolve('./flash-transmuxer-worker');
  496. } catch (e) {
  497. // no result
  498. }
  499. return result;
  500. };
  501. /**
  502. * A wrapper around the setTimeout function that uses
  503. * the flash constant time between ticks value.
  504. *
  505. * @param {Function} func the function callback to run
  506. * @private
  507. */
  508. var scheduleTick = function scheduleTick(func) {
  509. // Chrome doesn't invoke requestAnimationFrame callbacks
  510. // in background tabs, so use setTimeout.
  511. _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
  512. };
  513. /**
  514. * Generates a random string of max length 6
  515. *
  516. * @return {String} the randomly generated string
  517. * @function generateRandomString
  518. * @private
  519. */
  520. var generateRandomString = function generateRandomString() {
  521. return Math.random().toString(36).slice(2, 8);
  522. };
  523. /**
  524. * Round a number to a specified number of places much like
  525. * toFixed but return a number instead of a string representation.
  526. *
  527. * @param {Number} num A number
  528. * @param {Number} places The number of decimal places which to
  529. * round
  530. * @private
  531. */
  532. var toDecimalPlaces = function toDecimalPlaces(num, places) {
  533. if (typeof places !== 'number' || places < 0) {
  534. places = 0;
  535. }
  536. var scale = Math.pow(10, places);
  537. return Math.round(num * scale) / scale;
  538. };
  539. /**
  540. * A SourceBuffer implementation for Flash rather than HTML.
  541. *
  542. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  543. * @param {Object} mediaSource the flash media source
  544. * @class FlashSourceBuffer
  545. * @extends videojs.EventTarget
  546. */
  547. var FlashSourceBuffer = (function (_videojs$EventTarget) {
  548. _inherits(FlashSourceBuffer, _videojs$EventTarget);
  549. function FlashSourceBuffer(mediaSource) {
  550. var _this = this;
  551. _classCallCheck(this, FlashSourceBuffer);
  552. _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
  553. var encodedHeader = undefined;
  554. // Start off using the globally defined value but refine
  555. // as we append data into flash
  556. this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
  557. // byte arrays queued to be appended
  558. this.buffer_ = [];
  559. // the total number of queued bytes
  560. this.bufferSize_ = 0;
  561. // to be able to determine the correct position to seek to, we
  562. // need to retain information about the mapping between the
  563. // media timeline and PTS values
  564. this.basePtsOffset_ = NaN;
  565. this.mediaSource_ = mediaSource;
  566. this.audioBufferEnd_ = NaN;
  567. this.videoBufferEnd_ = NaN;
  568. // indicates whether the asynchronous continuation of an operation
  569. // is still being processed
  570. // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
  571. this.updating = false;
  572. this.timestampOffset_ = 0;
  573. encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
  574. // create function names with added randomness for the global callbacks flash will use
  575. // to get data from javascript into the swf. Random strings are added as a safety
  576. // measure for pages with multiple players since these functions will be global
  577. // instead of per instance. When making a call to the swf, the browser generates a
  578. // try catch code snippet, but just takes the function name and writes out an unquoted
  579. // call to that function. If the player id has any special characters, this will result
  580. // in an error, so safePlayerId replaces all special characters to '_'
  581. var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
  582. this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
  583. this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
  584. _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
  585. delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
  586. return encodedHeader;
  587. };
  588. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
  589. this.transmuxer_ = (0, _webwackify2['default'])(_flashTransmuxerWorker2['default'], resolveFlashTransmuxWorker());
  590. this.transmuxer_.postMessage({ action: 'init', options: {} });
  591. this.transmuxer_.onmessage = function (event) {
  592. if (event.data.action === 'data') {
  593. _this.receiveBuffer_(event.data.segment);
  594. }
  595. };
  596. this.one('updateend', function () {
  597. _this.mediaSource_.tech_.trigger('loadedmetadata');
  598. });
  599. Object.defineProperty(this, 'timestampOffset', {
  600. get: function get() {
  601. return this.timestampOffset_;
  602. },
  603. set: function set(val) {
  604. if (typeof val === 'number' && val >= 0) {
  605. this.timestampOffset_ = val;
  606. // We have to tell flash to expect a discontinuity
  607. this.mediaSource_.swfObj.vjs_discontinuity();
  608. // the media <-> PTS mapping must be re-established after
  609. // the discontinuity
  610. this.basePtsOffset_ = NaN;
  611. this.audioBufferEnd_ = NaN;
  612. this.videoBufferEnd_ = NaN;
  613. this.transmuxer_.postMessage({ action: 'reset' });
  614. }
  615. }
  616. });
  617. Object.defineProperty(this, 'buffered', {
  618. get: function get() {
  619. if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
  620. return _videoJs2['default'].createTimeRange();
  621. }
  622. var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
  623. if (buffered && buffered.length) {
  624. buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
  625. buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
  626. }
  627. return _videoJs2['default'].createTimeRanges(buffered);
  628. }
  629. });
  630. // On a seek we remove all text track data since flash has no concept
  631. // of a buffered-range and everything else is reset on seek
  632. this.mediaSource_.player_.on('seeked', function () {
  633. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
  634. if (_this.inbandTextTracks_) {
  635. for (var track in _this.inbandTextTracks_) {
  636. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]);
  637. }
  638. }
  639. });
  640. var onHlsReset = this.onHlsReset_.bind(this);
  641. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  642. // resets its state and flushes the buffer
  643. this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset);
  644. this.mediaSource_.player_.tech_.hls.on('dispose', function () {
  645. _this.transmuxer_.terminate();
  646. _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset);
  647. });
  648. }
  649. /**
  650. * Append bytes to the sourcebuffers buffer, in this case we
  651. * have to append it to swf object.
  652. *
  653. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  654. * @param {Array} bytes
  655. */
  656. _createClass(FlashSourceBuffer, [{
  657. key: 'appendBuffer',
  658. value: function appendBuffer(bytes) {
  659. var error = undefined;
  660. if (this.updating) {
  661. error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
  662. error.name = 'InvalidStateError';
  663. error.code = 11;
  664. throw error;
  665. }
  666. this.updating = true;
  667. this.mediaSource_.readyState = 'open';
  668. this.trigger({ type: 'update' });
  669. this.transmuxer_.postMessage({
  670. action: 'push',
  671. data: bytes.buffer,
  672. byteOffset: bytes.byteOffset,
  673. byteLength: bytes.byteLength
  674. }, [bytes.buffer]);
  675. this.transmuxer_.postMessage({ action: 'flush' });
  676. }
  677. /**
  678. * Reset the parser and remove any data queued to be sent to the SWF.
  679. *
  680. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  681. */
  682. }, {
  683. key: 'abort',
  684. value: function abort() {
  685. this.buffer_ = [];
  686. this.bufferSize_ = 0;
  687. this.mediaSource_.swfObj.vjs_abort();
  688. // report any outstanding updates have ended
  689. if (this.updating) {
  690. this.updating = false;
  691. this.trigger({ type: 'updateend' });
  692. }
  693. }
  694. /**
  695. * Flash cannot remove ranges already buffered in the NetStream
  696. * but seeking clears the buffer entirely. For most purposes,
  697. * having this operation act as a no-op is acceptable.
  698. *
  699. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  700. * @param {Double} start start of the section to remove
  701. * @param {Double} end end of the section to remove
  702. */
  703. }, {
  704. key: 'remove',
  705. value: function remove(start, end) {
  706. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  707. if (this.inbandTextTracks_) {
  708. for (var track in this.inbandTextTracks_) {
  709. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  710. }
  711. }
  712. this.trigger({ type: 'update' });
  713. this.trigger({ type: 'updateend' });
  714. }
  715. /**
  716. * Receive a buffer from the flv.
  717. *
  718. * @param {Object} segment
  719. * @private
  720. */
  721. }, {
  722. key: 'receiveBuffer_',
  723. value: function receiveBuffer_(segment) {
  724. var _this2 = this;
  725. // create an in-band caption track if one is present in the segment
  726. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  727. (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
  728. // Do this asynchronously since convertTagsToData_ can be time consuming
  729. scheduleTick(function () {
  730. var flvBytes = _this2.convertTagsToData_(segment);
  731. if (_this2.buffer_.length === 0) {
  732. scheduleTick(_this2.processBuffer_.bind(_this2));
  733. }
  734. if (flvBytes) {
  735. _this2.buffer_.push(flvBytes);
  736. _this2.bufferSize_ += flvBytes.byteLength;
  737. }
  738. });
  739. }
  740. /**
  741. * Append a portion of the current buffer to the SWF.
  742. *
  743. * @private
  744. */
  745. }, {
  746. key: 'processBuffer_',
  747. value: function processBuffer_() {
  748. var _this3 = this;
  749. var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
  750. if (!this.buffer_.length) {
  751. if (this.updating !== false) {
  752. this.updating = false;
  753. this.trigger({ type: 'updateend' });
  754. }
  755. // do nothing if the buffer is empty
  756. return;
  757. }
  758. // concatenate appends up to the max append size
  759. var chunk = this.buffer_[0].subarray(0, chunkSize);
  760. // requeue any bytes that won't make it this round
  761. if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
  762. this.buffer_.shift();
  763. } else {
  764. this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
  765. }
  766. this.bufferSize_ -= chunk.byteLength;
  767. // base64 encode the bytes
  768. var binary = [];
  769. var length = chunk.byteLength;
  770. for (var i = 0; i < length; i++) {
  771. binary.push(String.fromCharCode(chunk[i]));
  772. }
  773. var b64str = _globalWindow2['default'].btoa(binary.join(''));
  774. _globalWindow2['default'][this.flashEncodedDataName_] = function () {
  775. // schedule another processBuffer to process any left over data or to
  776. // trigger updateend
  777. scheduleTick(_this3.processBuffer_.bind(_this3));
  778. delete _globalWindow2['default'][_this3.flashEncodedDataName_];
  779. return b64str;
  780. };
  781. // Notify the swf that segment data is ready to be appended
  782. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
  783. }
  784. /**
  785. * Turns an array of flv tags into a Uint8Array representing the
  786. * flv data. Also removes any tags that are before the current
  787. * time so that playback begins at or slightly after the right
  788. * place on a seek
  789. *
  790. * @private
  791. * @param {Object} segmentData object of segment data
  792. */
  793. }, {
  794. key: 'convertTagsToData_',
  795. value: function convertTagsToData_(segmentData) {
  796. var segmentByteLength = 0;
  797. var tech = this.mediaSource_.tech_;
  798. var videoTargetPts = 0;
  799. var segment = undefined;
  800. var videoTags = segmentData.tags.videoTags;
  801. var audioTags = segmentData.tags.audioTags;
  802. // Establish the media timeline to PTS translation if we don't
  803. // have one already
  804. if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
  805. // We know there is at least one video or audio tag, but since we may not have both,
  806. // we use pts: Infinity for the missing tag. The will force the following Math.min
  807. // call will to use the proper pts value since it will always be less than Infinity
  808. var firstVideoTag = videoTags[0] || { pts: Infinity };
  809. var firstAudioTag = audioTags[0] || { pts: Infinity };
  810. this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
  811. }
  812. if (tech.seeking()) {
  813. // Do not use previously saved buffer end values while seeking since buffer
  814. // is cleared on all seeks
  815. this.videoBufferEnd_ = NaN;
  816. this.audioBufferEnd_ = NaN;
  817. }
  818. if (isNaN(this.videoBufferEnd_)) {
  819. if (tech.buffered().length) {
  820. videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
  821. }
  822. // Trim to currentTime if seeking
  823. if (tech.seeking()) {
  824. videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
  825. }
  826. // PTS values are represented in milliseconds
  827. videoTargetPts *= 1e3;
  828. videoTargetPts += this.basePtsOffset_;
  829. } else {
  830. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  831. // could append an overlapping segment, in which case there is a high likelyhood
  832. // a tag could have a matching pts to videoBufferEnd_, which would cause
  833. // that tag to get appended by the tag.pts >= targetPts check below even though it
  834. // is a duplicate of what was previously appended
  835. videoTargetPts = this.videoBufferEnd_ + 0.1;
  836. }
  837. // filter complete GOPs with a presentation time less than the seek target/end of buffer
  838. var currentIndex = videoTags.length;
  839. // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
  840. // since our videoTargetPts lies in a future segment
  841. if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
  842. // Start by walking backwards from the end of the list until we reach a tag that
  843. // is equal to or less than videoTargetPts
  844. while (--currentIndex) {
  845. var currentTag = videoTags[currentIndex];
  846. if (currentTag.pts > videoTargetPts) {
  847. continue;
  848. }
  849. // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
  850. // exit the loop as this is the start of the GOP that we want to append
  851. if (currentTag.keyFrame || currentTag.metaDataTag) {
  852. break;
  853. }
  854. }
  855. // We need to check if there are any metadata tags that come before currentIndex
  856. // as those will be metadata tags associated with the GOP we are appending
  857. // There could be 0 to 2 metadata tags that come before the currentIndex depending
  858. // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
  859. // key frame
  860. while (currentIndex) {
  861. var nextTag = videoTags[currentIndex - 1];
  862. if (!nextTag.metaDataTag) {
  863. break;
  864. }
  865. currentIndex--;
  866. }
  867. }
  868. var filteredVideoTags = videoTags.slice(currentIndex);
  869. var audioTargetPts = undefined;
  870. if (isNaN(this.audioBufferEnd_)) {
  871. audioTargetPts = videoTargetPts;
  872. } else {
  873. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  874. // could append an overlapping segment, in which case there is a high likelyhood
  875. // a tag could have a matching pts to videoBufferEnd_, which would cause
  876. // that tag to get appended by the tag.pts >= targetPts check below even though it
  877. // is a duplicate of what was previously appended
  878. audioTargetPts = this.audioBufferEnd_ + 0.1;
  879. }
  880. if (filteredVideoTags.length) {
  881. // If targetPts intersects a GOP and we appended the tags for the GOP that came
  882. // before targetPts, we want to make sure to trim audio tags at the pts
  883. // of the first video tag to avoid brief moments of silence
  884. audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
  885. }
  886. // skip tags with a presentation time less than the seek target/end of buffer
  887. currentIndex = 0;
  888. while (currentIndex < audioTags.length) {
  889. if (audioTags[currentIndex].pts >= audioTargetPts) {
  890. break;
  891. }
  892. currentIndex++;
  893. }
  894. var filteredAudioTags = audioTags.slice(currentIndex);
  895. // update the audio and video buffer ends
  896. if (filteredAudioTags.length) {
  897. this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
  898. }
  899. if (filteredVideoTags.length) {
  900. this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
  901. }
  902. var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
  903. if (tags.length === 0) {
  904. return;
  905. }
  906. // If we are appending data that comes before our target pts, we want to tell
  907. // the swf to adjust its notion of current time to account for the extra tags
  908. // we are appending to complete the GOP that intersects with targetPts
  909. if (tags[0].pts < videoTargetPts && tech.seeking()) {
  910. var fudgeFactor = 1 / 30;
  911. var currentTime = tech.currentTime();
  912. var diff = (videoTargetPts - tags[0].pts) / 1e3;
  913. var adjustedTime = currentTime - diff;
  914. if (adjustedTime < fudgeFactor) {
  915. adjustedTime = 0;
  916. }
  917. try {
  918. this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
  919. } catch (e) {
  920. // no-op for backwards compatability of swf. If adjustCurrentTime fails,
  921. // the swf may incorrectly report currentTime and buffered ranges
  922. // but should not affect playback over than the time displayed on the
  923. // progress bar is inaccurate
  924. }
  925. }
  926. // concatenate the bytes into a single segment
  927. for (var i = 0; i < tags.length; i++) {
  928. segmentByteLength += tags[i].bytes.byteLength;
  929. }
  930. segment = new Uint8Array(segmentByteLength);
  931. for (var i = 0, j = 0; i < tags.length; i++) {
  932. segment.set(tags[i].bytes, j);
  933. j += tags[i].bytes.byteLength;
  934. }
  935. return segment;
  936. }
  937. /**
  938. * Assemble the FLV tags in decoder order.
  939. *
  940. * @private
  941. * @param {Array} videoTags list of video tags
  942. * @param {Array} audioTags list of audio tags
  943. */
  944. }, {
  945. key: 'getOrderedTags_',
  946. value: function getOrderedTags_(videoTags, audioTags) {
  947. var tag = undefined;
  948. var tags = [];
  949. while (videoTags.length || audioTags.length) {
  950. if (!videoTags.length) {
  951. // only audio tags remain
  952. tag = audioTags.shift();
  953. } else if (!audioTags.length) {
  954. // only video tags remain
  955. tag = videoTags.shift();
  956. } else if (audioTags[0].dts < videoTags[0].dts) {
  957. // audio should be decoded next
  958. tag = audioTags.shift();
  959. } else {
  960. // video should be decoded next
  961. tag = videoTags.shift();
  962. }
  963. tags.push(tag);
  964. }
  965. return tags;
  966. }
  967. }, {
  968. key: 'onHlsReset_',
  969. value: function onHlsReset_() {
  970. this.transmuxer_.postMessage({ action: 'resetCaptions' });
  971. }
  972. }]);
  973. return FlashSourceBuffer;
  974. })(_videoJs2['default'].EventTarget);
  975. exports['default'] = FlashSourceBuffer;
  976. module.exports = exports['default'];
  977. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  978. },{"./add-text-track-data":1,"./create-text-tracks-if-necessary":3,"./flash-constants":4,"./flash-transmuxer-worker":7,"./remove-cues-from-track":9,"global/window":14,"mux.js/lib/flv":22,"webwackify":36}],7:[function(require,module,exports){
  979. /**
  980. * @file flash-transmuxer-worker.js
  981. */
  982. 'use strict';
  983. Object.defineProperty(exports, '__esModule', {
  984. value: true
  985. });
  986. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  987. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  988. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  989. var _globalWindow = require('global/window');
  990. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  991. var _muxJsLibFlv = require('mux.js/lib/flv');
  992. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  993. /**
  994. * Re-emits transmuxer events by converting them into messages to the
  995. * world outside the worker.
  996. *
  997. * @param {Object} transmuxer the transmuxer to wire events on
  998. * @private
  999. */
  1000. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  1001. transmuxer.on('data', function (segment) {
  1002. _globalWindow2['default'].postMessage({
  1003. action: 'data',
  1004. segment: segment
  1005. });
  1006. });
  1007. transmuxer.on('done', function (data) {
  1008. _globalWindow2['default'].postMessage({ action: 'done' });
  1009. });
  1010. };
  1011. /**
  1012. * All incoming messages route through this hash. If no function exists
  1013. * to handle an incoming message, then we ignore the message.
  1014. *
  1015. * @class MessageHandlers
  1016. * @param {Object} options the options to initialize with
  1017. */
  1018. var MessageHandlers = (function () {
  1019. function MessageHandlers(options) {
  1020. _classCallCheck(this, MessageHandlers);
  1021. this.options = options || {};
  1022. this.init();
  1023. }
  1024. /**
  1025. * Our web wroker interface so that things can talk to mux.js
  1026. * that will be running in a web worker. The scope is passed to this by
  1027. * webworkify.
  1028. *
  1029. * @param {Object} self the scope for the web worker
  1030. */
  1031. /**
  1032. * initialize our web worker and wire all the events.
  1033. */
  1034. _createClass(MessageHandlers, [{
  1035. key: 'init',
  1036. value: function init() {
  1037. if (this.transmuxer) {
  1038. this.transmuxer.dispose();
  1039. }
  1040. this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
  1041. wireTransmuxerEvents(this.transmuxer);
  1042. }
  1043. /**
  1044. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  1045. * processing.
  1046. *
  1047. * @param {ArrayBuffer} data data to push into the muxer
  1048. */
  1049. }, {
  1050. key: 'push',
  1051. value: function push(data) {
  1052. // Cast array buffer to correct type for transmuxer
  1053. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  1054. this.transmuxer.push(segment);
  1055. }
  1056. /**
  1057. * Recreate the transmuxer so that the next segment added via `push`
  1058. * start with a fresh transmuxer.
  1059. */
  1060. }, {
  1061. key: 'reset',
  1062. value: function reset() {
  1063. this.init();
  1064. }
  1065. /**
  1066. * Forces the pipeline to finish processing the last segment and emit its
  1067. * results.
  1068. */
  1069. }, {
  1070. key: 'flush',
  1071. value: function flush() {
  1072. this.transmuxer.flush();
  1073. }
  1074. }, {
  1075. key: 'resetCaptions',
  1076. value: function resetCaptions() {
  1077. this.transmuxer.resetCaptions();
  1078. }
  1079. }]);
  1080. return MessageHandlers;
  1081. })();
  1082. var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
  1083. self.onmessage = function (event) {
  1084. if (event.data.action === 'init' && event.data.options) {
  1085. this.messageHandlers = new MessageHandlers(event.data.options);
  1086. return;
  1087. }
  1088. if (!this.messageHandlers) {
  1089. this.messageHandlers = new MessageHandlers();
  1090. }
  1091. if (event.data && event.data.action && event.data.action !== 'init') {
  1092. if (this.messageHandlers[event.data.action]) {
  1093. this.messageHandlers[event.data.action](event.data);
  1094. }
  1095. }
  1096. };
  1097. };
  1098. exports['default'] = function (self) {
  1099. return new FlashTransmuxerWorker(self);
  1100. };
  1101. module.exports = exports['default'];
  1102. },{"global/window":14,"mux.js/lib/flv":22}],8:[function(require,module,exports){
  1103. (function (global){
  1104. /**
  1105. * @file html-media-source.js
  1106. */
  1107. 'use strict';
  1108. Object.defineProperty(exports, '__esModule', {
  1109. value: true
  1110. });
  1111. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1112. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  1113. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1114. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1115. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  1116. var _globalWindow = require('global/window');
  1117. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1118. var _globalDocument = require('global/document');
  1119. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  1120. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  1121. var _videoJs2 = _interopRequireDefault(_videoJs);
  1122. var _virtualSourceBuffer = require('./virtual-source-buffer');
  1123. var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
  1124. var _addTextTrackData = require('./add-text-track-data');
  1125. var _codecUtils = require('./codec-utils');
  1126. /**
  1127. * Our MediaSource implementation in HTML, mimics native
  1128. * MediaSource where/if possible.
  1129. *
  1130. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  1131. * @class HtmlMediaSource
  1132. * @extends videojs.EventTarget
  1133. */
  1134. var HtmlMediaSource = (function (_videojs$EventTarget) {
  1135. _inherits(HtmlMediaSource, _videojs$EventTarget);
  1136. function HtmlMediaSource() {
  1137. var _this = this;
  1138. _classCallCheck(this, HtmlMediaSource);
  1139. _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
  1140. var property = undefined;
  1141. this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
  1142. // delegate to the native MediaSource's methods by default
  1143. for (property in this.nativeMediaSource_) {
  1144. if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
  1145. this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
  1146. }
  1147. }
  1148. // emulate `duration` and `seekable` until seeking can be
  1149. // handled uniformly for live streams
  1150. // see https://github.com/w3c/media-source/issues/5
  1151. this.duration_ = NaN;
  1152. Object.defineProperty(this, 'duration', {
  1153. get: function get() {
  1154. if (this.duration_ === Infinity) {
  1155. return this.duration_;
  1156. }
  1157. return this.nativeMediaSource_.duration;
  1158. },
  1159. set: function set(duration) {
  1160. this.duration_ = duration;
  1161. if (duration !== Infinity) {
  1162. this.nativeMediaSource_.duration = duration;
  1163. return;
  1164. }
  1165. }
  1166. });
  1167. Object.defineProperty(this, 'seekable', {
  1168. get: function get() {
  1169. if (this.duration_ === Infinity) {
  1170. return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
  1171. }
  1172. return this.nativeMediaSource_.seekable;
  1173. }
  1174. });
  1175. Object.defineProperty(this, 'readyState', {
  1176. get: function get() {
  1177. return this.nativeMediaSource_.readyState;
  1178. }
  1179. });
  1180. Object.defineProperty(this, 'activeSourceBuffers', {
  1181. get: function get() {
  1182. return this.activeSourceBuffers_;
  1183. }
  1184. });
  1185. // the list of virtual and native SourceBuffers created by this
  1186. // MediaSource
  1187. this.sourceBuffers = [];
  1188. this.activeSourceBuffers_ = [];
  1189. /**
  1190. * update the list of active source buffers based upon various
  1191. * imformation from HLS and video.js
  1192. *
  1193. * @private
  1194. */
  1195. this.updateActiveSourceBuffers_ = function () {
  1196. // Retain the reference but empty the array
  1197. _this.activeSourceBuffers_.length = 0;
  1198. // If there is only one source buffer, then it will always be active and audio will
  1199. // be disabled based on the codec of the source buffer
  1200. if (_this.sourceBuffers.length === 1) {
  1201. var sourceBuffer = _this.sourceBuffers[0];
  1202. sourceBuffer.appendAudioInitSegment_ = true;
  1203. sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
  1204. _this.activeSourceBuffers_.push(sourceBuffer);
  1205. return;
  1206. }
  1207. // There are 2 source buffers, a combined (possibly video only) source buffer and
  1208. // and an audio only source buffer.
  1209. // By default, the audio in the combined virtual source buffer is enabled
  1210. // and the audio-only source buffer (if it exists) is disabled.
  1211. var disableCombined = false;
  1212. var disableAudioOnly = true;
  1213. // TODO: maybe we can store the sourcebuffers on the track objects?
  1214. // safari may do something like this
  1215. for (var i = 0; i < _this.player_.audioTracks().length; i++) {
  1216. var track = _this.player_.audioTracks()[i];
  1217. if (track.enabled && track.kind !== 'main') {
  1218. // The enabled track is an alternate audio track so disable the audio in
  1219. // the combined source buffer and enable the audio-only source buffer.
  1220. disableCombined = true;
  1221. disableAudioOnly = false;
  1222. break;
  1223. }
  1224. }
  1225. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1226. /* eslinst-disable */
  1227. // TODO once codecs are required, we can switch to using the codecs to determine
  1228. // what stream is the video stream, rather than relying on videoTracks
  1229. /* eslinst-enable */
  1230. sourceBuffer.appendAudioInitSegment_ = true;
  1231. if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  1232. // combined
  1233. sourceBuffer.audioDisabled_ = disableCombined;
  1234. } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
  1235. // If the "combined" source buffer is video only, then we do not want
  1236. // disable the audio-only source buffer (this is mostly for demuxed
  1237. // audio and video hls)
  1238. sourceBuffer.audioDisabled_ = true;
  1239. disableAudioOnly = false;
  1240. } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  1241. // audio only
  1242. sourceBuffer.audioDisabled_ = disableAudioOnly;
  1243. if (disableAudioOnly) {
  1244. return;
  1245. }
  1246. }
  1247. _this.activeSourceBuffers_.push(sourceBuffer);
  1248. });
  1249. };
  1250. this.onPlayerMediachange_ = function () {
  1251. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1252. sourceBuffer.appendAudioInitSegment_ = true;
  1253. });
  1254. };
  1255. this.onHlsReset_ = function () {
  1256. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1257. if (sourceBuffer.transmuxer_) {
  1258. sourceBuffer.transmuxer_.postMessage({ action: 'resetCaptions' });
  1259. }
  1260. });
  1261. };
  1262. this.onHlsSegmentTimeMapping_ = function (event) {
  1263. _this.sourceBuffers.forEach(function (buffer) {
  1264. return buffer.timeMapping_ = event.mapping;
  1265. });
  1266. };
  1267. // Re-emit MediaSource events on the polyfill
  1268. ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
  1269. this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
  1270. }, this);
  1271. // capture the associated player when the MediaSource is
  1272. // successfully attached
  1273. this.on('sourceopen', function (event) {
  1274. // Get the player this MediaSource is attached to
  1275. var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
  1276. if (!video) {
  1277. return;
  1278. }
  1279. _this.player_ = (0, _videoJs2['default'])(video.parentNode);
  1280. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  1281. // resets its state and flushes the buffer
  1282. _this.player_.tech_.on('hls-reset', _this.onHlsReset_);
  1283. // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
  1284. // SegmentLoader inspects an MTS segment and has an accurate stream to display
  1285. // time mapping
  1286. _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
  1287. if (_this.player_.audioTracks && _this.player_.audioTracks()) {
  1288. _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
  1289. _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
  1290. _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
  1291. }
  1292. _this.player_.on('mediachange', _this.onPlayerMediachange_);
  1293. });
  1294. this.on('sourceended', function (event) {
  1295. var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
  1296. for (var i = 0; i < _this.sourceBuffers.length; i++) {
  1297. var sourcebuffer = _this.sourceBuffers[i];
  1298. var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
  1299. if (cues && cues.length) {
  1300. cues[cues.length - 1].endTime = duration;
  1301. }
  1302. }
  1303. });
  1304. // explicitly terminate any WebWorkers that were created
  1305. // by SourceHandlers
  1306. this.on('sourceclose', function (event) {
  1307. this.sourceBuffers.forEach(function (sourceBuffer) {
  1308. if (sourceBuffer.transmuxer_) {
  1309. sourceBuffer.transmuxer_.terminate();
  1310. }
  1311. });
  1312. this.sourceBuffers.length = 0;
  1313. if (!this.player_) {
  1314. return;
  1315. }
  1316. if (this.player_.audioTracks && this.player_.audioTracks()) {
  1317. this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
  1318. this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
  1319. this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
  1320. }
  1321. // We can only change this if the player hasn't been disposed of yet
  1322. // because `off` eventually tries to use the el_ property. If it has
  1323. // been disposed of, then don't worry about it because there are no
  1324. // event handlers left to unbind anyway
  1325. if (this.player_.el_) {
  1326. this.player_.off('mediachange', this.onPlayerMediachange_);
  1327. this.player_.tech_.off('hls-reset', this.onHlsReset_);
  1328. this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
  1329. }
  1330. });
  1331. }
  1332. /**
  1333. * Add a range that that can now be seeked to.
  1334. *
  1335. * @param {Double} start where to start the addition
  1336. * @param {Double} end where to end the addition
  1337. * @private
  1338. */
  1339. _createClass(HtmlMediaSource, [{
  1340. key: 'addSeekableRange_',
  1341. value: function addSeekableRange_(start, end) {
  1342. var error = undefined;
  1343. if (this.duration !== Infinity) {
  1344. error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
  1345. error.name = 'InvalidStateError';
  1346. error.code = 11;
  1347. throw error;
  1348. }
  1349. if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
  1350. this.nativeMediaSource_.duration = end;
  1351. }
  1352. }
  1353. /**
  1354. * Add a source buffer to the media source.
  1355. *
  1356. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  1357. * @param {String} type the content-type of the content
  1358. * @return {Object} the created source buffer
  1359. */
  1360. }, {
  1361. key: 'addSourceBuffer',
  1362. value: function addSourceBuffer(type) {
  1363. var buffer = undefined;
  1364. var parsedType = (0, _codecUtils.parseContentType)(type);
  1365. // Create a VirtualSourceBuffer to transmux MPEG-2 transport
  1366. // stream segments into fragmented MP4s
  1367. if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
  1368. var codecs = [];
  1369. if (parsedType.parameters && parsedType.parameters.codecs) {
  1370. codecs = parsedType.parameters.codecs.split(',');
  1371. codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
  1372. codecs = codecs.filter(function (codec) {
  1373. return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
  1374. });
  1375. }
  1376. if (codecs.length === 0) {
  1377. codecs = ['avc1.4d400d', 'mp4a.40.2'];
  1378. }
  1379. buffer = new _virtualSourceBuffer2['default'](this, codecs);
  1380. if (this.sourceBuffers.length !== 0) {
  1381. // If another VirtualSourceBuffer already exists, then we are creating a
  1382. // SourceBuffer for an alternate audio track and therefore we know that
  1383. // the source has both an audio and video track.
  1384. // That means we should trigger the manual creation of the real
  1385. // SourceBuffers instead of waiting for the transmuxer to return data
  1386. this.sourceBuffers[0].createRealSourceBuffers_();
  1387. buffer.createRealSourceBuffers_();
  1388. // Automatically disable the audio on the first source buffer if
  1389. // a second source buffer is ever created
  1390. this.sourceBuffers[0].audioDisabled_ = true;
  1391. }
  1392. } else {
  1393. // delegate to the native implementation
  1394. buffer = this.nativeMediaSource_.addSourceBuffer(type);
  1395. }
  1396. this.sourceBuffers.push(buffer);
  1397. return buffer;
  1398. }
  1399. }]);
  1400. return HtmlMediaSource;
  1401. })(_videoJs2['default'].EventTarget);
  1402. exports['default'] = HtmlMediaSource;
  1403. module.exports = exports['default'];
  1404. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  1405. },{"./add-text-track-data":1,"./codec-utils":2,"./virtual-source-buffer":11,"global/document":13,"global/window":14}],9:[function(require,module,exports){
  1406. /**
  1407. * @file remove-cues-from-track.js
  1408. */
  1409. /**
  1410. * Remove cues from a track on video.js.
  1411. *
  1412. * @param {Double} start start of where we should remove the cue
  1413. * @param {Double} end end of where the we should remove the cue
  1414. * @param {Object} track the text track to remove the cues from
  1415. * @private
  1416. */
  1417. "use strict";
  1418. Object.defineProperty(exports, "__esModule", {
  1419. value: true
  1420. });
  1421. var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
  1422. var i = undefined;
  1423. var cue = undefined;
  1424. if (!track) {
  1425. return;
  1426. }
  1427. if (!track.cues) {
  1428. return;
  1429. }
  1430. i = track.cues.length;
  1431. while (i--) {
  1432. cue = track.cues[i];
  1433. // Remove any overlapping cue
  1434. if (cue.startTime <= end && cue.endTime >= start) {
  1435. track.removeCue(cue);
  1436. }
  1437. }
  1438. };
  1439. exports["default"] = removeCuesFromTrack;
  1440. module.exports = exports["default"];
  1441. },{}],10:[function(require,module,exports){
  1442. /**
  1443. * @file transmuxer-worker.js
  1444. */
  1445. /**
  1446. * videojs-contrib-media-sources
  1447. *
  1448. * Copyright (c) 2015 Brightcove
  1449. * All rights reserved.
  1450. *
  1451. * Handles communication between the browser-world and the mux.js
  1452. * transmuxer running inside of a WebWorker by exposing a simple
  1453. * message-based interface to a Transmuxer object.
  1454. */
  1455. 'use strict';
  1456. Object.defineProperty(exports, '__esModule', {
  1457. value: true
  1458. });
  1459. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1460. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1461. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1462. var _globalWindow = require('global/window');
  1463. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1464. var _muxJsLibMp4 = require('mux.js/lib/mp4');
  1465. var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
  1466. /**
  1467. * Re-emits transmuxer events by converting them into messages to the
  1468. * world outside the worker.
  1469. *
  1470. * @param {Object} transmuxer the transmuxer to wire events on
  1471. * @private
  1472. */
  1473. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  1474. transmuxer.on('data', function (segment) {
  1475. // transfer ownership of the underlying ArrayBuffer
  1476. // instead of doing a copy to save memory
  1477. // ArrayBuffers are transferable but generic TypedArrays are not
  1478. // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
  1479. var initArray = segment.initSegment;
  1480. segment.initSegment = {
  1481. data: initArray.buffer,
  1482. byteOffset: initArray.byteOffset,
  1483. byteLength: initArray.byteLength
  1484. };
  1485. var typedArray = segment.data;
  1486. segment.data = typedArray.buffer;
  1487. _globalWindow2['default'].postMessage({
  1488. action: 'data',
  1489. segment: segment,
  1490. byteOffset: typedArray.byteOffset,
  1491. byteLength: typedArray.byteLength
  1492. }, [segment.data]);
  1493. });
  1494. if (transmuxer.captionStream) {
  1495. transmuxer.captionStream.on('data', function (caption) {
  1496. _globalWindow2['default'].postMessage({
  1497. action: 'caption',
  1498. data: caption
  1499. });
  1500. });
  1501. }
  1502. transmuxer.on('done', function (data) {
  1503. _globalWindow2['default'].postMessage({ action: 'done' });
  1504. });
  1505. transmuxer.on('gopInfo', function (gopInfo) {
  1506. _globalWindow2['default'].postMessage({
  1507. action: 'gopInfo',
  1508. gopInfo: gopInfo
  1509. });
  1510. });
  1511. };
  1512. /**
  1513. * All incoming messages route through this hash. If no function exists
  1514. * to handle an incoming message, then we ignore the message.
  1515. *
  1516. * @class MessageHandlers
  1517. * @param {Object} options the options to initialize with
  1518. */
  1519. var MessageHandlers = (function () {
  1520. function MessageHandlers(options) {
  1521. _classCallCheck(this, MessageHandlers);
  1522. this.options = options || {};
  1523. this.init();
  1524. }
  1525. /**
  1526. * Our web wroker interface so that things can talk to mux.js
  1527. * that will be running in a web worker. the scope is passed to this by
  1528. * webworkify.
  1529. *
  1530. * @param {Object} self the scope for the web worker
  1531. */
  1532. /**
  1533. * initialize our web worker and wire all the events.
  1534. */
  1535. _createClass(MessageHandlers, [{
  1536. key: 'init',
  1537. value: function init() {
  1538. if (this.transmuxer) {
  1539. this.transmuxer.dispose();
  1540. }
  1541. this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
  1542. wireTransmuxerEvents(this.transmuxer);
  1543. }
  1544. /**
  1545. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  1546. * processing.
  1547. *
  1548. * @param {ArrayBuffer} data data to push into the muxer
  1549. */
  1550. }, {
  1551. key: 'push',
  1552. value: function push(data) {
  1553. // Cast array buffer to correct type for transmuxer
  1554. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  1555. this.transmuxer.push(segment);
  1556. }
  1557. /**
  1558. * Recreate the transmuxer so that the next segment added via `push`
  1559. * start with a fresh transmuxer.
  1560. */
  1561. }, {
  1562. key: 'reset',
  1563. value: function reset() {
  1564. this.init();
  1565. }
  1566. /**
  1567. * Set the value that will be used as the `baseMediaDecodeTime` time for the
  1568. * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
  1569. * set relative to the first based on the PTS values.
  1570. *
  1571. * @param {Object} data used to set the timestamp offset in the muxer
  1572. */
  1573. }, {
  1574. key: 'setTimestampOffset',
  1575. value: function setTimestampOffset(data) {
  1576. var timestampOffset = data.timestampOffset || 0;
  1577. this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
  1578. }
  1579. }, {
  1580. key: 'setAudioAppendStart',
  1581. value: function setAudioAppendStart(data) {
  1582. this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
  1583. }
  1584. /**
  1585. * Forces the pipeline to finish processing the last segment and emit it's
  1586. * results.
  1587. *
  1588. * @param {Object} data event data, not really used
  1589. */
  1590. }, {
  1591. key: 'flush',
  1592. value: function flush(data) {
  1593. this.transmuxer.flush();
  1594. }
  1595. }, {
  1596. key: 'resetCaptions',
  1597. value: function resetCaptions() {
  1598. this.transmuxer.resetCaptions();
  1599. }
  1600. }, {
  1601. key: 'alignGopsWith',
  1602. value: function alignGopsWith(data) {
  1603. this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
  1604. }
  1605. }]);
  1606. return MessageHandlers;
  1607. })();
  1608. var TransmuxerWorker = function TransmuxerWorker(self) {
  1609. self.onmessage = function (event) {
  1610. if (event.data.action === 'init' && event.data.options) {
  1611. this.messageHandlers = new MessageHandlers(event.data.options);
  1612. return;
  1613. }
  1614. if (!this.messageHandlers) {
  1615. this.messageHandlers = new MessageHandlers();
  1616. }
  1617. if (event.data && event.data.action && event.data.action !== 'init') {
  1618. if (this.messageHandlers[event.data.action]) {
  1619. this.messageHandlers[event.data.action](event.data);
  1620. }
  1621. }
  1622. };
  1623. };
  1624. exports['default'] = function (self) {
  1625. return new TransmuxerWorker(self);
  1626. };
  1627. module.exports = exports['default'];
  1628. },{"global/window":14,"mux.js/lib/mp4":30}],11:[function(require,module,exports){
  1629. (function (global){
  1630. /**
  1631. * @file virtual-source-buffer.js
  1632. */
  1633. 'use strict';
  1634. Object.defineProperty(exports, '__esModule', {
  1635. value: true
  1636. });
  1637. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1638. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  1639. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1640. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1641. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  1642. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  1643. var _videoJs2 = _interopRequireDefault(_videoJs);
  1644. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  1645. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  1646. var _removeCuesFromTrack = require('./remove-cues-from-track');
  1647. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  1648. var _addTextTrackData = require('./add-text-track-data');
  1649. var _webwackify = require('webwackify');
  1650. var _webwackify2 = _interopRequireDefault(_webwackify);
  1651. var _transmuxerWorker = require('./transmuxer-worker');
  1652. var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
  1653. var _codecUtils = require('./codec-utils');
  1654. var resolveTransmuxWorker = function resolveTransmuxWorker() {
  1655. var result = undefined;
  1656. try {
  1657. result = require.resolve('./transmuxer-worker');
  1658. } catch (e) {
  1659. // no result
  1660. }
  1661. return result;
  1662. };
  1663. // We create a wrapper around the SourceBuffer so that we can manage the
  1664. // state of the `updating` property manually. We have to do this because
  1665. // Firefox changes `updating` to false long before triggering `updateend`
  1666. // events and that was causing strange problems in videojs-contrib-hls
  1667. var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
  1668. var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
  1669. var wrapper = Object.create(null);
  1670. wrapper.updating = false;
  1671. wrapper.realBuffer_ = sourceBuffer;
  1672. var _loop = function (key) {
  1673. if (typeof sourceBuffer[key] === 'function') {
  1674. wrapper[key] = function () {
  1675. return sourceBuffer[key].apply(sourceBuffer, arguments);
  1676. };
  1677. } else if (typeof wrapper[key] === 'undefined') {
  1678. Object.defineProperty(wrapper, key, {
  1679. get: function get() {
  1680. return sourceBuffer[key];
  1681. },
  1682. set: function set(v) {
  1683. return sourceBuffer[key] = v;
  1684. }
  1685. });
  1686. }
  1687. };
  1688. for (var key in sourceBuffer) {
  1689. _loop(key);
  1690. }
  1691. return wrapper;
  1692. };
  1693. /**
  1694. * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
  1695. * front of current time.
  1696. *
  1697. * @param {Array} buffer
  1698. * The current buffer of gop information
  1699. * @param {Player} player
  1700. * The player instance
  1701. * @param {Double} mapping
  1702. * Offset to map display time to stream presentation time
  1703. * @return {Array}
  1704. * List of gops considered safe to append over
  1705. */
  1706. var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, player, mapping) {
  1707. if (!player || !buffer.length) {
  1708. return [];
  1709. }
  1710. // pts value for current time + 3 seconds to give a bit more wiggle room
  1711. var currentTimePts = Math.ceil((player.currentTime() - mapping + 3) * 90000);
  1712. var i = undefined;
  1713. for (i = 0; i < buffer.length; i++) {
  1714. if (buffer[i].pts > currentTimePts) {
  1715. break;
  1716. }
  1717. }
  1718. return buffer.slice(i);
  1719. };
  1720. exports.gopsSafeToAlignWith = gopsSafeToAlignWith;
  1721. /**
  1722. * Appends gop information (timing and byteLength) received by the transmuxer for the
  1723. * gops appended in the last call to appendBuffer
  1724. *
  1725. * @param {Array} buffer
  1726. * The current buffer of gop information
  1727. * @param {Array} gops
  1728. * List of new gop information
  1729. * @param {boolean} replace
  1730. * If true, replace the buffer with the new gop information. If false, append the
  1731. * new gop information to the buffer in the right location of time.
  1732. * @return {Array}
  1733. * Updated list of gop information
  1734. */
  1735. var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
  1736. if (!gops.length) {
  1737. return buffer;
  1738. }
  1739. if (replace) {
  1740. // If we are in safe append mode, then completely overwrite the gop buffer
  1741. // with the most recent appeneded data. This will make sure that when appending
  1742. // future segments, we only try to align with gops that are both ahead of current
  1743. // time and in the last segment appended.
  1744. return gops.slice();
  1745. }
  1746. var start = gops[0].pts;
  1747. var i = 0;
  1748. for (i; i < buffer.length; i++) {
  1749. if (buffer[i].pts >= start) {
  1750. break;
  1751. }
  1752. }
  1753. return buffer.slice(0, i).concat(gops);
  1754. };
  1755. exports.updateGopBuffer = updateGopBuffer;
  1756. /**
  1757. * Removes gop information in buffer that overlaps with provided start and end
  1758. *
  1759. * @param {Array} buffer
  1760. * The current buffer of gop information
  1761. * @param {Double} start
  1762. * position to start the remove at
  1763. * @param {Double} end
  1764. * position to end the remove at
  1765. * @param {Double} mapping
  1766. * Offset to map display time to stream presentation time
  1767. */
  1768. var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
  1769. var startPts = Math.ceil((start - mapping) * 90000);
  1770. var endPts = Math.ceil((end - mapping) * 90000);
  1771. var updatedBuffer = buffer.slice();
  1772. var i = buffer.length;
  1773. while (i--) {
  1774. if (buffer[i].pts <= endPts) {
  1775. break;
  1776. }
  1777. }
  1778. if (i === -1) {
  1779. // no removal because end of remove range is before start of buffer
  1780. return updatedBuffer;
  1781. }
  1782. var j = i + 1;
  1783. while (j--) {
  1784. if (buffer[j].pts <= startPts) {
  1785. break;
  1786. }
  1787. }
  1788. // clamp remove range start to 0 index
  1789. j = Math.max(j, 0);
  1790. updatedBuffer.splice(j, i - j + 1);
  1791. return updatedBuffer;
  1792. };
  1793. exports.removeGopBuffer = removeGopBuffer;
  1794. /**
  1795. * VirtualSourceBuffers exist so that we can transmux non native formats
  1796. * into a native format, but keep the same api as a native source buffer.
  1797. * It creates a transmuxer, that works in its own thread (a web worker) and
  1798. * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
  1799. * then send all of that data to the naive sourcebuffer so that it is
  1800. * indestinguishable from a natively supported format.
  1801. *
  1802. * @param {HtmlMediaSource} mediaSource the parent mediaSource
  1803. * @param {Array} codecs array of codecs that we will be dealing with
  1804. * @class VirtualSourceBuffer
  1805. * @extends video.js.EventTarget
  1806. */
  1807. var VirtualSourceBuffer = (function (_videojs$EventTarget) {
  1808. _inherits(VirtualSourceBuffer, _videojs$EventTarget);
  1809. function VirtualSourceBuffer(mediaSource, codecs) {
  1810. var _this = this;
  1811. _classCallCheck(this, VirtualSourceBuffer);
  1812. _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
  1813. this.timestampOffset_ = 0;
  1814. this.pendingBuffers_ = [];
  1815. this.bufferUpdating_ = false;
  1816. this.mediaSource_ = mediaSource;
  1817. this.codecs_ = codecs;
  1818. this.audioCodec_ = null;
  1819. this.videoCodec_ = null;
  1820. this.audioDisabled_ = false;
  1821. this.appendAudioInitSegment_ = true;
  1822. this.gopBuffer_ = [];
  1823. this.timeMapping_ = 0;
  1824. this.safeAppend_ = _videoJs2['default'].browser.IE_VERSION >= 11;
  1825. var options = {
  1826. remux: false,
  1827. alignGopsAtEnd: this.safeAppend_
  1828. };
  1829. this.codecs_.forEach(function (codec) {
  1830. if ((0, _codecUtils.isAudioCodec)(codec)) {
  1831. _this.audioCodec_ = codec;
  1832. } else if ((0, _codecUtils.isVideoCodec)(codec)) {
  1833. _this.videoCodec_ = codec;
  1834. }
  1835. });
  1836. // append muxed segments to their respective native buffers as
  1837. // soon as they are available
  1838. this.transmuxer_ = (0, _webwackify2['default'])(_transmuxerWorker2['default'], resolveTransmuxWorker());
  1839. this.transmuxer_.postMessage({ action: 'init', options: options });
  1840. this.transmuxer_.onmessage = function (event) {
  1841. if (event.data.action === 'data') {
  1842. return _this.data_(event);
  1843. }
  1844. if (event.data.action === 'done') {
  1845. return _this.done_(event);
  1846. }
  1847. if (event.data.action === 'gopInfo') {
  1848. return _this.appendGopInfo_(event);
  1849. }
  1850. };
  1851. // this timestampOffset is a property with the side-effect of resetting
  1852. // baseMediaDecodeTime in the transmuxer on the setter
  1853. Object.defineProperty(this, 'timestampOffset', {
  1854. get: function get() {
  1855. return this.timestampOffset_;
  1856. },
  1857. set: function set(val) {
  1858. if (typeof val === 'number' && val >= 0) {
  1859. this.timestampOffset_ = val;
  1860. this.appendAudioInitSegment_ = true;
  1861. // reset gop buffer on timestampoffset as this signals a change in timeline
  1862. this.gopBuffer_.length = 0;
  1863. this.timeMapping_ = 0;
  1864. // We have to tell the transmuxer to set the baseMediaDecodeTime to
  1865. // the desired timestampOffset for the next segment
  1866. this.transmuxer_.postMessage({
  1867. action: 'setTimestampOffset',
  1868. timestampOffset: val
  1869. });
  1870. }
  1871. }
  1872. });
  1873. // setting the append window affects both source buffers
  1874. Object.defineProperty(this, 'appendWindowStart', {
  1875. get: function get() {
  1876. return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
  1877. },
  1878. set: function set(start) {
  1879. if (this.videoBuffer_) {
  1880. this.videoBuffer_.appendWindowStart = start;
  1881. }
  1882. if (this.audioBuffer_) {
  1883. this.audioBuffer_.appendWindowStart = start;
  1884. }
  1885. }
  1886. });
  1887. // this buffer is "updating" if either of its native buffers are
  1888. Object.defineProperty(this, 'updating', {
  1889. get: function get() {
  1890. return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
  1891. }
  1892. });
  1893. // the buffered property is the intersection of the buffered
  1894. // ranges of the native source buffers
  1895. Object.defineProperty(this, 'buffered', {
  1896. get: function get() {
  1897. var start = null;
  1898. var end = null;
  1899. var arity = 0;
  1900. var extents = [];
  1901. var ranges = [];
  1902. // neither buffer has been created yet
  1903. if (!this.videoBuffer_ && !this.audioBuffer_) {
  1904. return _videoJs2['default'].createTimeRange();
  1905. }
  1906. // only one buffer is configured
  1907. if (!this.videoBuffer_) {
  1908. return this.audioBuffer_.buffered;
  1909. }
  1910. if (!this.audioBuffer_) {
  1911. return this.videoBuffer_.buffered;
  1912. }
  1913. // both buffers are configured
  1914. if (this.audioDisabled_) {
  1915. return this.videoBuffer_.buffered;
  1916. }
  1917. // both buffers are empty
  1918. if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
  1919. return _videoJs2['default'].createTimeRange();
  1920. }
  1921. // Handle the case where we have both buffers and create an
  1922. // intersection of the two
  1923. var videoBuffered = this.videoBuffer_.buffered;
  1924. var audioBuffered = this.audioBuffer_.buffered;
  1925. var count = videoBuffered.length;
  1926. // A) Gather up all start and end times
  1927. while (count--) {
  1928. extents.push({ time: videoBuffered.start(count), type: 'start' });
  1929. extents.push({ time: videoBuffered.end(count), type: 'end' });
  1930. }
  1931. count = audioBuffered.length;
  1932. while (count--) {
  1933. extents.push({ time: audioBuffered.start(count), type: 'start' });
  1934. extents.push({ time: audioBuffered.end(count), type: 'end' });
  1935. }
  1936. // B) Sort them by time
  1937. extents.sort(function (a, b) {
  1938. return a.time - b.time;
  1939. });
  1940. // C) Go along one by one incrementing arity for start and decrementing
  1941. // arity for ends
  1942. for (count = 0; count < extents.length; count++) {
  1943. if (extents[count].type === 'start') {
  1944. arity++;
  1945. // D) If arity is ever incremented to 2 we are entering an
  1946. // overlapping range
  1947. if (arity === 2) {
  1948. start = extents[count].time;
  1949. }
  1950. } else if (extents[count].type === 'end') {
  1951. arity--;
  1952. // E) If arity is ever decremented to 1 we leaving an
  1953. // overlapping range
  1954. if (arity === 1) {
  1955. end = extents[count].time;
  1956. }
  1957. }
  1958. // F) Record overlapping ranges
  1959. if (start !== null && end !== null) {
  1960. ranges.push([start, end]);
  1961. start = null;
  1962. end = null;
  1963. }
  1964. }
  1965. return _videoJs2['default'].createTimeRanges(ranges);
  1966. }
  1967. });
  1968. }
  1969. /**
  1970. * When we get a data event from the transmuxer
  1971. * we call this function and handle the data that
  1972. * was sent to us
  1973. *
  1974. * @private
  1975. * @param {Event} event the data event from the transmuxer
  1976. */
  1977. _createClass(VirtualSourceBuffer, [{
  1978. key: 'data_',
  1979. value: function data_(event) {
  1980. var segment = event.data.segment;
  1981. // Cast ArrayBuffer to TypedArray
  1982. segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
  1983. segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
  1984. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  1985. // Add the segments to the pendingBuffers array
  1986. this.pendingBuffers_.push(segment);
  1987. return;
  1988. }
  1989. /**
  1990. * When we get a done event from the transmuxer
  1991. * we call this function and we process all
  1992. * of the pending data that we have been saving in the
  1993. * data_ function
  1994. *
  1995. * @private
  1996. * @param {Event} event the done event from the transmuxer
  1997. */
  1998. }, {
  1999. key: 'done_',
  2000. value: function done_(event) {
  2001. // Don't process and append data if the mediaSource is closed
  2002. if (this.mediaSource_.readyState === 'closed') {
  2003. this.pendingBuffers_.length = 0;
  2004. return;
  2005. }
  2006. // All buffers should have been flushed from the muxer
  2007. // start processing anything we have received
  2008. this.processPendingSegments_();
  2009. return;
  2010. }
  2011. /**
  2012. * Create our internal native audio/video source buffers and add
  2013. * event handlers to them with the following conditions:
  2014. * 1. they do not already exist on the mediaSource
  2015. * 2. this VSB has a codec for them
  2016. *
  2017. * @private
  2018. */
  2019. }, {
  2020. key: 'createRealSourceBuffers_',
  2021. value: function createRealSourceBuffers_() {
  2022. var _this2 = this;
  2023. var types = ['audio', 'video'];
  2024. types.forEach(function (type) {
  2025. // Don't create a SourceBuffer of this type if we don't have a
  2026. // codec for it
  2027. if (!_this2[type + 'Codec_']) {
  2028. return;
  2029. }
  2030. // Do nothing if a SourceBuffer of this type already exists
  2031. if (_this2[type + 'Buffer_']) {
  2032. return;
  2033. }
  2034. var buffer = null;
  2035. // If the mediasource already has a SourceBuffer for the codec
  2036. // use that
  2037. if (_this2.mediaSource_[type + 'Buffer_']) {
  2038. buffer = _this2.mediaSource_[type + 'Buffer_'];
  2039. // In multiple audio track cases, the audio source buffer is disabled
  2040. // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
  2041. // than createRealSourceBuffers_ is called to create the second
  2042. // VirtualSourceBuffer because that happens as a side-effect of
  2043. // videojs-contrib-hls starting the audioSegmentLoader. As a result,
  2044. // the audioBuffer is essentially "ownerless" and no one will toggle
  2045. // the `updating` state back to false once the `updateend` event is received
  2046. //
  2047. // Setting `updating` to false manually will work around this
  2048. // situation and allow work to continue
  2049. buffer.updating = false;
  2050. } else {
  2051. var codecProperty = type + 'Codec_';
  2052. var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
  2053. buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
  2054. _this2.mediaSource_[type + 'Buffer_'] = buffer;
  2055. }
  2056. _this2[type + 'Buffer_'] = buffer;
  2057. // Wire up the events to the SourceBuffer
  2058. ['update', 'updatestart', 'updateend'].forEach(function (event) {
  2059. buffer.addEventListener(event, function () {
  2060. // if audio is disabled
  2061. if (type === 'audio' && _this2.audioDisabled_) {
  2062. return;
  2063. }
  2064. if (event === 'updateend') {
  2065. _this2[type + 'Buffer_'].updating = false;
  2066. }
  2067. var shouldTrigger = types.every(function (t) {
  2068. // skip checking audio's updating status if audio
  2069. // is not enabled
  2070. if (t === 'audio' && _this2.audioDisabled_) {
  2071. return true;
  2072. }
  2073. // if the other type if updating we don't trigger
  2074. if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
  2075. return false;
  2076. }
  2077. return true;
  2078. });
  2079. if (shouldTrigger) {
  2080. return _this2.trigger(event);
  2081. }
  2082. });
  2083. });
  2084. });
  2085. }
  2086. /**
  2087. * Emulate the native mediasource function, but our function will
  2088. * send all of the proposed segments to the transmuxer so that we
  2089. * can transmux them before we append them to our internal
  2090. * native source buffers in the correct format.
  2091. *
  2092. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  2093. * @param {Uint8Array} segment the segment to append to the buffer
  2094. */
  2095. }, {
  2096. key: 'appendBuffer',
  2097. value: function appendBuffer(segment) {
  2098. // Start the internal "updating" state
  2099. this.bufferUpdating_ = true;
  2100. if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
  2101. var audioBuffered = this.audioBuffer_.buffered;
  2102. this.transmuxer_.postMessage({
  2103. action: 'setAudioAppendStart',
  2104. appendStart: audioBuffered.end(audioBuffered.length - 1)
  2105. });
  2106. }
  2107. if (this.videoBuffer_) {
  2108. this.transmuxer_.postMessage({
  2109. action: 'alignGopsWith',
  2110. gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_, this.timeMapping_)
  2111. });
  2112. }
  2113. this.transmuxer_.postMessage({
  2114. action: 'push',
  2115. // Send the typed-array of data as an ArrayBuffer so that
  2116. // it can be sent as a "Transferable" and avoid the costly
  2117. // memory copy
  2118. data: segment.buffer,
  2119. // To recreate the original typed-array, we need information
  2120. // about what portion of the ArrayBuffer it was a view into
  2121. byteOffset: segment.byteOffset,
  2122. byteLength: segment.byteLength
  2123. }, [segment.buffer]);
  2124. this.transmuxer_.postMessage({ action: 'flush' });
  2125. }
  2126. /**
  2127. * Appends gop information (timing and byteLength) received by the transmuxer for the
  2128. * gops appended in the last call to appendBuffer
  2129. *
  2130. * @param {Event} event
  2131. * The gopInfo event from the transmuxer
  2132. * @param {Array} event.data.gopInfo
  2133. * List of gop info to append
  2134. */
  2135. }, {
  2136. key: 'appendGopInfo_',
  2137. value: function appendGopInfo_(event) {
  2138. this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
  2139. }
  2140. /**
  2141. * Emulate the native mediasource function and remove parts
  2142. * of the buffer from any of our internal buffers that exist
  2143. *
  2144. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  2145. * @param {Double} start position to start the remove at
  2146. * @param {Double} end position to end the remove at
  2147. */
  2148. }, {
  2149. key: 'remove',
  2150. value: function remove(start, end) {
  2151. if (this.videoBuffer_) {
  2152. this.videoBuffer_.updating = true;
  2153. this.videoBuffer_.remove(start, end);
  2154. this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
  2155. }
  2156. if (!this.audioDisabled_ && this.audioBuffer_) {
  2157. this.audioBuffer_.updating = true;
  2158. this.audioBuffer_.remove(start, end);
  2159. }
  2160. // Remove Metadata Cues (id3)
  2161. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  2162. // Remove Any Captions
  2163. if (this.inbandTextTracks_) {
  2164. for (var track in this.inbandTextTracks_) {
  2165. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  2166. }
  2167. }
  2168. }
  2169. /**
  2170. * Process any segments that the muxer has output
  2171. * Concatenate segments together based on type and append them into
  2172. * their respective sourceBuffers
  2173. *
  2174. * @private
  2175. */
  2176. }, {
  2177. key: 'processPendingSegments_',
  2178. value: function processPendingSegments_() {
  2179. var sortedSegments = {
  2180. video: {
  2181. segments: [],
  2182. bytes: 0
  2183. },
  2184. audio: {
  2185. segments: [],
  2186. bytes: 0
  2187. },
  2188. captions: [],
  2189. metadata: []
  2190. };
  2191. // Sort segments into separate video/audio arrays and
  2192. // keep track of their total byte lengths
  2193. sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
  2194. var type = segment.type;
  2195. var data = segment.data;
  2196. var initSegment = segment.initSegment;
  2197. segmentObj[type].segments.push(data);
  2198. segmentObj[type].bytes += data.byteLength;
  2199. segmentObj[type].initSegment = initSegment;
  2200. // Gather any captions into a single array
  2201. if (segment.captions) {
  2202. segmentObj.captions = segmentObj.captions.concat(segment.captions);
  2203. }
  2204. if (segment.info) {
  2205. segmentObj[type].info = segment.info;
  2206. }
  2207. // Gather any metadata into a single array
  2208. if (segment.metadata) {
  2209. segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
  2210. }
  2211. return segmentObj;
  2212. }, sortedSegments);
  2213. // Create the real source buffers if they don't exist by now since we
  2214. // finally are sure what tracks are contained in the source
  2215. if (!this.videoBuffer_ && !this.audioBuffer_) {
  2216. // Remove any codecs that may have been specified by default but
  2217. // are no longer applicable now
  2218. if (sortedSegments.video.bytes === 0) {
  2219. this.videoCodec_ = null;
  2220. }
  2221. if (sortedSegments.audio.bytes === 0) {
  2222. this.audioCodec_ = null;
  2223. }
  2224. this.createRealSourceBuffers_();
  2225. }
  2226. if (sortedSegments.audio.info) {
  2227. this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
  2228. }
  2229. if (sortedSegments.video.info) {
  2230. this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
  2231. }
  2232. if (this.appendAudioInitSegment_) {
  2233. if (!this.audioDisabled_ && this.audioBuffer_) {
  2234. sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
  2235. sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
  2236. }
  2237. this.appendAudioInitSegment_ = false;
  2238. }
  2239. var triggerUpdateend = false;
  2240. // Merge multiple video and audio segments into one and append
  2241. if (this.videoBuffer_ && sortedSegments.video.bytes) {
  2242. sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
  2243. sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
  2244. this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
  2245. // TODO: are video tracks the only ones with text tracks?
  2246. (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
  2247. } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
  2248. // The transmuxer did not return any bytes of video, meaning it was all trimmed
  2249. // for gop alignment. Since we have a video buffer and audio is disabled, updateend
  2250. // will never be triggered by this source buffer, which will cause contrib-hls
  2251. // to be stuck forever waiting for updateend. If audio is not disabled, updateend
  2252. // will be triggered by the audio buffer, which will be sent upwards since the video
  2253. // buffer will not be in an updating state.
  2254. triggerUpdateend = true;
  2255. }
  2256. if (!this.audioDisabled_ && this.audioBuffer_) {
  2257. this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
  2258. }
  2259. this.pendingBuffers_.length = 0;
  2260. if (triggerUpdateend) {
  2261. this.trigger('updateend');
  2262. }
  2263. // We are no longer in the internal "updating" state
  2264. this.bufferUpdating_ = false;
  2265. }
  2266. /**
  2267. * Combine all segments into a single Uint8Array and then append them
  2268. * to the destination buffer
  2269. *
  2270. * @param {Object} segmentObj
  2271. * @param {SourceBuffer} destinationBuffer native source buffer to append data to
  2272. * @private
  2273. */
  2274. }, {
  2275. key: 'concatAndAppendSegments_',
  2276. value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
  2277. var offset = 0;
  2278. var tempBuffer = undefined;
  2279. if (segmentObj.bytes) {
  2280. tempBuffer = new Uint8Array(segmentObj.bytes);
  2281. // Combine the individual segments into one large typed-array
  2282. segmentObj.segments.forEach(function (segment) {
  2283. tempBuffer.set(segment, offset);
  2284. offset += segment.byteLength;
  2285. });
  2286. try {
  2287. destinationBuffer.updating = true;
  2288. destinationBuffer.appendBuffer(tempBuffer);
  2289. } catch (error) {
  2290. if (this.mediaSource_.player_) {
  2291. this.mediaSource_.player_.error({
  2292. code: -3,
  2293. type: 'APPEND_BUFFER_ERR',
  2294. message: error.message,
  2295. originalError: error
  2296. });
  2297. }
  2298. }
  2299. }
  2300. }
  2301. /**
  2302. * Emulate the native mediasource function. abort any soureBuffer
  2303. * actions and throw out any un-appended data.
  2304. *
  2305. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  2306. */
  2307. }, {
  2308. key: 'abort',
  2309. value: function abort() {
  2310. if (this.videoBuffer_) {
  2311. this.videoBuffer_.abort();
  2312. }
  2313. if (!this.audioDisabled_ && this.audioBuffer_) {
  2314. this.audioBuffer_.abort();
  2315. }
  2316. if (this.transmuxer_) {
  2317. this.transmuxer_.postMessage({ action: 'reset' });
  2318. }
  2319. this.pendingBuffers_.length = 0;
  2320. this.bufferUpdating_ = false;
  2321. }
  2322. }]);
  2323. return VirtualSourceBuffer;
  2324. })(_videoJs2['default'].EventTarget);
  2325. exports['default'] = VirtualSourceBuffer;
  2326. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2327. },{"./add-text-track-data":1,"./codec-utils":2,"./create-text-tracks-if-necessary":3,"./remove-cues-from-track":9,"./transmuxer-worker":10,"webwackify":36}],12:[function(require,module,exports){
  2328. },{}],13:[function(require,module,exports){
  2329. (function (global){
  2330. var topLevel = typeof global !== 'undefined' ? global :
  2331. typeof window !== 'undefined' ? window : {}
  2332. var minDoc = require('min-document');
  2333. var doccy;
  2334. if (typeof document !== 'undefined') {
  2335. doccy = document;
  2336. } else {
  2337. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  2338. if (!doccy) {
  2339. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  2340. }
  2341. }
  2342. module.exports = doccy;
  2343. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2344. },{"min-document":12}],14:[function(require,module,exports){
  2345. (function (global){
  2346. var win;
  2347. if (typeof window !== "undefined") {
  2348. win = window;
  2349. } else if (typeof global !== "undefined") {
  2350. win = global;
  2351. } else if (typeof self !== "undefined"){
  2352. win = self;
  2353. } else {
  2354. win = {};
  2355. }
  2356. module.exports = win;
  2357. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2358. },{}],15:[function(require,module,exports){
  2359. /**
  2360. * mux.js
  2361. *
  2362. * Copyright (c) 2016 Brightcove
  2363. * All rights reserved.
  2364. *
  2365. * A stream-based aac to mp4 converter. This utility can be used to
  2366. * deliver mp4s to a SourceBuffer on platforms that support native
  2367. * Media Source Extensions.
  2368. */
  2369. 'use strict';
  2370. var Stream = require('../utils/stream.js');
  2371. // Constants
  2372. var AacStream;
  2373. /**
  2374. * Splits an incoming stream of binary data into ADTS and ID3 Frames.
  2375. */
  2376. AacStream = function() {
  2377. var
  2378. everything = new Uint8Array(),
  2379. timeStamp = 0;
  2380. AacStream.prototype.init.call(this);
  2381. this.setTimestamp = function(timestamp) {
  2382. timeStamp = timestamp;
  2383. };
  2384. this.parseId3TagSize = function(header, byteIndex) {
  2385. var
  2386. returnSize = (header[byteIndex + 6] << 21) |
  2387. (header[byteIndex + 7] << 14) |
  2388. (header[byteIndex + 8] << 7) |
  2389. (header[byteIndex + 9]),
  2390. flags = header[byteIndex + 5],
  2391. footerPresent = (flags & 16) >> 4;
  2392. if (footerPresent) {
  2393. return returnSize + 20;
  2394. }
  2395. return returnSize + 10;
  2396. };
  2397. this.parseAdtsSize = function(header, byteIndex) {
  2398. var
  2399. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  2400. middle = header[byteIndex + 4] << 3,
  2401. highTwo = header[byteIndex + 3] & 0x3 << 11;
  2402. return (highTwo | middle) | lowThree;
  2403. };
  2404. this.push = function(bytes) {
  2405. var
  2406. frameSize = 0,
  2407. byteIndex = 0,
  2408. bytesLeft,
  2409. chunk,
  2410. packet,
  2411. tempLength;
  2412. // If there are bytes remaining from the last segment, prepend them to the
  2413. // bytes that were pushed in
  2414. if (everything.length) {
  2415. tempLength = everything.length;
  2416. everything = new Uint8Array(bytes.byteLength + tempLength);
  2417. everything.set(everything.subarray(0, tempLength));
  2418. everything.set(bytes, tempLength);
  2419. } else {
  2420. everything = bytes;
  2421. }
  2422. while (everything.length - byteIndex >= 3) {
  2423. if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
  2424. (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  2425. (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
  2426. // Exit early because we don't have enough to parse
  2427. // the ID3 tag header
  2428. if (everything.length - byteIndex < 10) {
  2429. break;
  2430. }
  2431. // check framesize
  2432. frameSize = this.parseId3TagSize(everything, byteIndex);
  2433. // Exit early if we don't have enough in the buffer
  2434. // to emit a full packet
  2435. if (frameSize > everything.length) {
  2436. break;
  2437. }
  2438. chunk = {
  2439. type: 'timed-metadata',
  2440. data: everything.subarray(byteIndex, byteIndex + frameSize)
  2441. };
  2442. this.trigger('data', chunk);
  2443. byteIndex += frameSize;
  2444. continue;
  2445. } else if ((everything[byteIndex] & 0xff === 0xff) &&
  2446. ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
  2447. // Exit early because we don't have enough to parse
  2448. // the ADTS frame header
  2449. if (everything.length - byteIndex < 7) {
  2450. break;
  2451. }
  2452. frameSize = this.parseAdtsSize(everything, byteIndex);
  2453. // Exit early if we don't have enough in the buffer
  2454. // to emit a full packet
  2455. if (frameSize > everything.length) {
  2456. break;
  2457. }
  2458. packet = {
  2459. type: 'audio',
  2460. data: everything.subarray(byteIndex, byteIndex + frameSize),
  2461. pts: timeStamp,
  2462. dts: timeStamp
  2463. };
  2464. this.trigger('data', packet);
  2465. byteIndex += frameSize;
  2466. continue;
  2467. }
  2468. byteIndex++;
  2469. }
  2470. bytesLeft = everything.length - byteIndex;
  2471. if (bytesLeft > 0) {
  2472. everything = everything.subarray(byteIndex);
  2473. } else {
  2474. everything = new Uint8Array();
  2475. }
  2476. };
  2477. };
  2478. AacStream.prototype = new Stream();
  2479. module.exports = AacStream;
  2480. },{"../utils/stream.js":35}],16:[function(require,module,exports){
  2481. 'use strict';
  2482. var Stream = require('../utils/stream.js');
  2483. var AdtsStream;
  2484. var
  2485. ADTS_SAMPLING_FREQUENCIES = [
  2486. 96000,
  2487. 88200,
  2488. 64000,
  2489. 48000,
  2490. 44100,
  2491. 32000,
  2492. 24000,
  2493. 22050,
  2494. 16000,
  2495. 12000,
  2496. 11025,
  2497. 8000,
  2498. 7350
  2499. ];
  2500. /*
  2501. * Accepts a ElementaryStream and emits data events with parsed
  2502. * AAC Audio Frames of the individual packets. Input audio in ADTS
  2503. * format is unpacked and re-emitted as AAC frames.
  2504. *
  2505. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  2506. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  2507. */
  2508. AdtsStream = function() {
  2509. var buffer;
  2510. AdtsStream.prototype.init.call(this);
  2511. this.push = function(packet) {
  2512. var
  2513. i = 0,
  2514. frameNum = 0,
  2515. frameLength,
  2516. protectionSkipBytes,
  2517. frameEnd,
  2518. oldBuffer,
  2519. sampleCount,
  2520. adtsFrameDuration;
  2521. if (packet.type !== 'audio') {
  2522. // ignore non-audio data
  2523. return;
  2524. }
  2525. // Prepend any data in the buffer to the input data so that we can parse
  2526. // aac frames the cross a PES packet boundary
  2527. if (buffer) {
  2528. oldBuffer = buffer;
  2529. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  2530. buffer.set(oldBuffer);
  2531. buffer.set(packet.data, oldBuffer.byteLength);
  2532. } else {
  2533. buffer = packet.data;
  2534. }
  2535. // unpack any ADTS frames which have been fully received
  2536. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  2537. while (i + 5 < buffer.length) {
  2538. // Loook for the start of an ADTS header..
  2539. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  2540. // If a valid header was not found, jump one forward and attempt to
  2541. // find a valid ADTS header starting at the next byte
  2542. i++;
  2543. continue;
  2544. }
  2545. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  2546. // end of the ADTS header
  2547. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  2548. // Frame length is a 13 bit integer starting 16 bits from the
  2549. // end of the sync sequence
  2550. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  2551. (buffer[i + 4] << 3) |
  2552. ((buffer[i + 5] & 0xe0) >> 5);
  2553. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  2554. adtsFrameDuration = (sampleCount * 90000) /
  2555. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  2556. frameEnd = i + frameLength;
  2557. // If we don't have enough data to actually finish this ADTS frame, return
  2558. // and wait for more data
  2559. if (buffer.byteLength < frameEnd) {
  2560. return;
  2561. }
  2562. // Otherwise, deliver the complete AAC frame
  2563. this.trigger('data', {
  2564. pts: packet.pts + (frameNum * adtsFrameDuration),
  2565. dts: packet.dts + (frameNum * adtsFrameDuration),
  2566. sampleCount: sampleCount,
  2567. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  2568. channelcount: ((buffer[i + 2] & 1) << 2) |
  2569. ((buffer[i + 3] & 0xc0) >>> 6),
  2570. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  2571. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  2572. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  2573. samplesize: 16,
  2574. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  2575. });
  2576. // If the buffer is empty, clear it and return
  2577. if (buffer.byteLength === frameEnd) {
  2578. buffer = undefined;
  2579. return;
  2580. }
  2581. frameNum++;
  2582. // Remove the finished frame from the buffer and start the process again
  2583. buffer = buffer.subarray(frameEnd);
  2584. }
  2585. };
  2586. this.flush = function() {
  2587. this.trigger('done');
  2588. };
  2589. };
  2590. AdtsStream.prototype = new Stream();
  2591. module.exports = AdtsStream;
  2592. },{"../utils/stream.js":35}],17:[function(require,module,exports){
  2593. 'use strict';
  2594. var Stream = require('../utils/stream.js');
  2595. var ExpGolomb = require('../utils/exp-golomb.js');
  2596. var H264Stream, NalByteStream;
  2597. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  2598. /**
  2599. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  2600. */
  2601. NalByteStream = function() {
  2602. var
  2603. syncPoint = 0,
  2604. i,
  2605. buffer;
  2606. NalByteStream.prototype.init.call(this);
  2607. this.push = function(data) {
  2608. var swapBuffer;
  2609. if (!buffer) {
  2610. buffer = data.data;
  2611. } else {
  2612. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  2613. swapBuffer.set(buffer);
  2614. swapBuffer.set(data.data, buffer.byteLength);
  2615. buffer = swapBuffer;
  2616. }
  2617. // Rec. ITU-T H.264, Annex B
  2618. // scan for NAL unit boundaries
  2619. // a match looks like this:
  2620. // 0 0 1 .. NAL .. 0 0 1
  2621. // ^ sync point ^ i
  2622. // or this:
  2623. // 0 0 1 .. NAL .. 0 0 0
  2624. // ^ sync point ^ i
  2625. // advance the sync point to a NAL start, if necessary
  2626. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  2627. if (buffer[syncPoint + 2] === 1) {
  2628. // the sync point is properly aligned
  2629. i = syncPoint + 5;
  2630. break;
  2631. }
  2632. }
  2633. while (i < buffer.byteLength) {
  2634. // look at the current byte to determine if we've hit the end of
  2635. // a NAL unit boundary
  2636. switch (buffer[i]) {
  2637. case 0:
  2638. // skip past non-sync sequences
  2639. if (buffer[i - 1] !== 0) {
  2640. i += 2;
  2641. break;
  2642. } else if (buffer[i - 2] !== 0) {
  2643. i++;
  2644. break;
  2645. }
  2646. // deliver the NAL unit if it isn't empty
  2647. if (syncPoint + 3 !== i - 2) {
  2648. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  2649. }
  2650. // drop trailing zeroes
  2651. do {
  2652. i++;
  2653. } while (buffer[i] !== 1 && i < buffer.length);
  2654. syncPoint = i - 2;
  2655. i += 3;
  2656. break;
  2657. case 1:
  2658. // skip past non-sync sequences
  2659. if (buffer[i - 1] !== 0 ||
  2660. buffer[i - 2] !== 0) {
  2661. i += 3;
  2662. break;
  2663. }
  2664. // deliver the NAL unit
  2665. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  2666. syncPoint = i - 2;
  2667. i += 3;
  2668. break;
  2669. default:
  2670. // the current byte isn't a one or zero, so it cannot be part
  2671. // of a sync sequence
  2672. i += 3;
  2673. break;
  2674. }
  2675. }
  2676. // filter out the NAL units that were delivered
  2677. buffer = buffer.subarray(syncPoint);
  2678. i -= syncPoint;
  2679. syncPoint = 0;
  2680. };
  2681. this.flush = function() {
  2682. // deliver the last buffered NAL unit
  2683. if (buffer && buffer.byteLength > 3) {
  2684. this.trigger('data', buffer.subarray(syncPoint + 3));
  2685. }
  2686. // reset the stream state
  2687. buffer = null;
  2688. syncPoint = 0;
  2689. this.trigger('done');
  2690. };
  2691. };
  2692. NalByteStream.prototype = new Stream();
  2693. // values of profile_idc that indicate additional fields are included in the SPS
  2694. // see Recommendation ITU-T H.264 (4/2013),
  2695. // 7.3.2.1.1 Sequence parameter set data syntax
  2696. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  2697. 100: true,
  2698. 110: true,
  2699. 122: true,
  2700. 244: true,
  2701. 44: true,
  2702. 83: true,
  2703. 86: true,
  2704. 118: true,
  2705. 128: true,
  2706. 138: true,
  2707. 139: true,
  2708. 134: true
  2709. };
  2710. /**
  2711. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  2712. * events.
  2713. */
  2714. H264Stream = function() {
  2715. var
  2716. nalByteStream = new NalByteStream(),
  2717. self,
  2718. trackId,
  2719. currentPts,
  2720. currentDts,
  2721. discardEmulationPreventionBytes,
  2722. readSequenceParameterSet,
  2723. skipScalingList;
  2724. H264Stream.prototype.init.call(this);
  2725. self = this;
  2726. this.push = function(packet) {
  2727. if (packet.type !== 'video') {
  2728. return;
  2729. }
  2730. trackId = packet.trackId;
  2731. currentPts = packet.pts;
  2732. currentDts = packet.dts;
  2733. nalByteStream.push(packet);
  2734. };
  2735. nalByteStream.on('data', function(data) {
  2736. var
  2737. event = {
  2738. trackId: trackId,
  2739. pts: currentPts,
  2740. dts: currentDts,
  2741. data: data
  2742. };
  2743. switch (data[0] & 0x1f) {
  2744. case 0x05:
  2745. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  2746. break;
  2747. case 0x06:
  2748. event.nalUnitType = 'sei_rbsp';
  2749. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  2750. break;
  2751. case 0x07:
  2752. event.nalUnitType = 'seq_parameter_set_rbsp';
  2753. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  2754. event.config = readSequenceParameterSet(event.escapedRBSP);
  2755. break;
  2756. case 0x08:
  2757. event.nalUnitType = 'pic_parameter_set_rbsp';
  2758. break;
  2759. case 0x09:
  2760. event.nalUnitType = 'access_unit_delimiter_rbsp';
  2761. break;
  2762. default:
  2763. break;
  2764. }
  2765. self.trigger('data', event);
  2766. });
  2767. nalByteStream.on('done', function() {
  2768. self.trigger('done');
  2769. });
  2770. this.flush = function() {
  2771. nalByteStream.flush();
  2772. };
  2773. /**
  2774. * Advance the ExpGolomb decoder past a scaling list. The scaling
  2775. * list is optionally transmitted as part of a sequence parameter
  2776. * set and is not relevant to transmuxing.
  2777. * @param count {number} the number of entries in this scaling list
  2778. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  2779. * start of a scaling list
  2780. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  2781. */
  2782. skipScalingList = function(count, expGolombDecoder) {
  2783. var
  2784. lastScale = 8,
  2785. nextScale = 8,
  2786. j,
  2787. deltaScale;
  2788. for (j = 0; j < count; j++) {
  2789. if (nextScale !== 0) {
  2790. deltaScale = expGolombDecoder.readExpGolomb();
  2791. nextScale = (lastScale + deltaScale + 256) % 256;
  2792. }
  2793. lastScale = (nextScale === 0) ? lastScale : nextScale;
  2794. }
  2795. };
  2796. /**
  2797. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  2798. * Sequence Payload"
  2799. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  2800. * unit
  2801. * @return {Uint8Array} the RBSP without any Emulation
  2802. * Prevention Bytes
  2803. */
  2804. discardEmulationPreventionBytes = function(data) {
  2805. var
  2806. length = data.byteLength,
  2807. emulationPreventionBytesPositions = [],
  2808. i = 1,
  2809. newLength, newData;
  2810. // Find all `Emulation Prevention Bytes`
  2811. while (i < length - 2) {
  2812. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  2813. emulationPreventionBytesPositions.push(i + 2);
  2814. i += 2;
  2815. } else {
  2816. i++;
  2817. }
  2818. }
  2819. // If no Emulation Prevention Bytes were found just return the original
  2820. // array
  2821. if (emulationPreventionBytesPositions.length === 0) {
  2822. return data;
  2823. }
  2824. // Create a new array to hold the NAL unit data
  2825. newLength = length - emulationPreventionBytesPositions.length;
  2826. newData = new Uint8Array(newLength);
  2827. var sourceIndex = 0;
  2828. for (i = 0; i < newLength; sourceIndex++, i++) {
  2829. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  2830. // Skip this byte
  2831. sourceIndex++;
  2832. // Remove this position index
  2833. emulationPreventionBytesPositions.shift();
  2834. }
  2835. newData[i] = data[sourceIndex];
  2836. }
  2837. return newData;
  2838. };
  2839. /**
  2840. * Read a sequence parameter set and return some interesting video
  2841. * properties. A sequence parameter set is the H264 metadata that
  2842. * describes the properties of upcoming video frames.
  2843. * @param data {Uint8Array} the bytes of a sequence parameter set
  2844. * @return {object} an object with configuration parsed from the
  2845. * sequence parameter set, including the dimensions of the
  2846. * associated video frames.
  2847. */
  2848. readSequenceParameterSet = function(data) {
  2849. var
  2850. frameCropLeftOffset = 0,
  2851. frameCropRightOffset = 0,
  2852. frameCropTopOffset = 0,
  2853. frameCropBottomOffset = 0,
  2854. sarScale = 1,
  2855. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  2856. chromaFormatIdc, picOrderCntType,
  2857. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  2858. picHeightInMapUnitsMinus1,
  2859. frameMbsOnlyFlag,
  2860. scalingListCount,
  2861. sarRatio,
  2862. aspectRatioIdc,
  2863. i;
  2864. expGolombDecoder = new ExpGolomb(data);
  2865. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  2866. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  2867. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  2868. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  2869. // some profiles have more optional data we don't need
  2870. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  2871. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  2872. if (chromaFormatIdc === 3) {
  2873. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  2874. }
  2875. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  2876. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  2877. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  2878. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  2879. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  2880. for (i = 0; i < scalingListCount; i++) {
  2881. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  2882. if (i < 6) {
  2883. skipScalingList(16, expGolombDecoder);
  2884. } else {
  2885. skipScalingList(64, expGolombDecoder);
  2886. }
  2887. }
  2888. }
  2889. }
  2890. }
  2891. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  2892. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  2893. if (picOrderCntType === 0) {
  2894. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  2895. } else if (picOrderCntType === 1) {
  2896. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  2897. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  2898. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  2899. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  2900. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  2901. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  2902. }
  2903. }
  2904. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  2905. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  2906. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  2907. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  2908. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  2909. if (frameMbsOnlyFlag === 0) {
  2910. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  2911. }
  2912. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  2913. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  2914. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  2915. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  2916. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  2917. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  2918. }
  2919. if (expGolombDecoder.readBoolean()) {
  2920. // vui_parameters_present_flag
  2921. if (expGolombDecoder.readBoolean()) {
  2922. // aspect_ratio_info_present_flag
  2923. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  2924. switch (aspectRatioIdc) {
  2925. case 1: sarRatio = [1, 1]; break;
  2926. case 2: sarRatio = [12, 11]; break;
  2927. case 3: sarRatio = [10, 11]; break;
  2928. case 4: sarRatio = [16, 11]; break;
  2929. case 5: sarRatio = [40, 33]; break;
  2930. case 6: sarRatio = [24, 11]; break;
  2931. case 7: sarRatio = [20, 11]; break;
  2932. case 8: sarRatio = [32, 11]; break;
  2933. case 9: sarRatio = [80, 33]; break;
  2934. case 10: sarRatio = [18, 11]; break;
  2935. case 11: sarRatio = [15, 11]; break;
  2936. case 12: sarRatio = [64, 33]; break;
  2937. case 13: sarRatio = [160, 99]; break;
  2938. case 14: sarRatio = [4, 3]; break;
  2939. case 15: sarRatio = [3, 2]; break;
  2940. case 16: sarRatio = [2, 1]; break;
  2941. case 255: {
  2942. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  2943. expGolombDecoder.readUnsignedByte(),
  2944. expGolombDecoder.readUnsignedByte() << 8 |
  2945. expGolombDecoder.readUnsignedByte() ];
  2946. break;
  2947. }
  2948. }
  2949. if (sarRatio) {
  2950. sarScale = sarRatio[0] / sarRatio[1];
  2951. }
  2952. }
  2953. }
  2954. return {
  2955. profileIdc: profileIdc,
  2956. levelIdc: levelIdc,
  2957. profileCompatibility: profileCompatibility,
  2958. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  2959. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  2960. };
  2961. };
  2962. };
  2963. H264Stream.prototype = new Stream();
  2964. module.exports = {
  2965. H264Stream: H264Stream,
  2966. NalByteStream: NalByteStream
  2967. };
  2968. },{"../utils/exp-golomb.js":34,"../utils/stream.js":35}],18:[function(require,module,exports){
  2969. var highPrefix = [33, 16, 5, 32, 164, 27];
  2970. var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
  2971. var zeroFill = function(count) {
  2972. var a = [];
  2973. while (count--) {
  2974. a.push(0);
  2975. }
  2976. return a;
  2977. };
  2978. var makeTable = function(metaTable) {
  2979. return Object.keys(metaTable).reduce(function(obj, key) {
  2980. obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
  2981. return arr.concat(part);
  2982. }, []));
  2983. return obj;
  2984. }, {});
  2985. };
  2986. // Frames-of-silence to use for filling in missing AAC frames
  2987. var coneOfSilence = {
  2988. 96000: [highPrefix, [227, 64], zeroFill(154), [56]],
  2989. 88200: [highPrefix, [231], zeroFill(170), [56]],
  2990. 64000: [highPrefix, [248, 192], zeroFill(240), [56]],
  2991. 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
  2992. 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
  2993. 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
  2994. 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
  2995. 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
  2996. 12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
  2997. 11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
  2998. 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
  2999. };
  3000. module.exports = makeTable(coneOfSilence);
  3001. },{}],19:[function(require,module,exports){
  3002. 'use strict';
  3003. var Stream = require('../utils/stream.js');
  3004. /**
  3005. * The final stage of the transmuxer that emits the flv tags
  3006. * for audio, video, and metadata. Also tranlates in time and
  3007. * outputs caption data and id3 cues.
  3008. */
  3009. var CoalesceStream = function(options) {
  3010. // Number of Tracks per output segment
  3011. // If greater than 1, we combine multiple
  3012. // tracks into a single segment
  3013. this.numberOfTracks = 0;
  3014. this.metadataStream = options.metadataStream;
  3015. this.videoTags = [];
  3016. this.audioTags = [];
  3017. this.videoTrack = null;
  3018. this.audioTrack = null;
  3019. this.pendingCaptions = [];
  3020. this.pendingMetadata = [];
  3021. this.pendingTracks = 0;
  3022. this.processedTracks = 0;
  3023. CoalesceStream.prototype.init.call(this);
  3024. // Take output from multiple
  3025. this.push = function(output) {
  3026. // buffer incoming captions until the associated video segment
  3027. // finishes
  3028. if (output.text) {
  3029. return this.pendingCaptions.push(output);
  3030. }
  3031. // buffer incoming id3 tags until the final flush
  3032. if (output.frames) {
  3033. return this.pendingMetadata.push(output);
  3034. }
  3035. if (output.track.type === 'video') {
  3036. this.videoTrack = output.track;
  3037. this.videoTags = output.tags;
  3038. this.pendingTracks++;
  3039. }
  3040. if (output.track.type === 'audio') {
  3041. this.audioTrack = output.track;
  3042. this.audioTags = output.tags;
  3043. this.pendingTracks++;
  3044. }
  3045. };
  3046. };
  3047. CoalesceStream.prototype = new Stream();
  3048. CoalesceStream.prototype.flush = function(flushSource) {
  3049. var
  3050. id3,
  3051. caption,
  3052. i,
  3053. timelineStartPts,
  3054. event = {
  3055. tags: {},
  3056. captions: [],
  3057. captionStreams: {},
  3058. metadata: []
  3059. };
  3060. if (this.pendingTracks < this.numberOfTracks) {
  3061. if (flushSource !== 'VideoSegmentStream' &&
  3062. flushSource !== 'AudioSegmentStream') {
  3063. // Return because we haven't received a flush from a data-generating
  3064. // portion of the segment (meaning that we have only recieved meta-data
  3065. // or captions.)
  3066. return;
  3067. } else if (this.pendingTracks === 0) {
  3068. // In the case where we receive a flush without any data having been
  3069. // received we consider it an emitted track for the purposes of coalescing
  3070. // `done` events.
  3071. // We do this for the case where there is an audio and video track in the
  3072. // segment but no audio data. (seen in several playlists with alternate
  3073. // audio tracks and no audio present in the main TS segments.)
  3074. this.processedTracks++;
  3075. if (this.processedTracks < this.numberOfTracks) {
  3076. return;
  3077. }
  3078. }
  3079. }
  3080. this.processedTracks += this.pendingTracks;
  3081. this.pendingTracks = 0;
  3082. if (this.processedTracks < this.numberOfTracks) {
  3083. return;
  3084. }
  3085. if (this.videoTrack) {
  3086. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  3087. } else if (this.audioTrack) {
  3088. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  3089. }
  3090. event.tags.videoTags = this.videoTags;
  3091. event.tags.audioTags = this.audioTags;
  3092. // Translate caption PTS times into second offsets into the
  3093. // video timeline for the segment, and add track info
  3094. for (i = 0; i < this.pendingCaptions.length; i++) {
  3095. caption = this.pendingCaptions[i];
  3096. caption.startTime = caption.startPts - timelineStartPts;
  3097. caption.startTime /= 90e3;
  3098. caption.endTime = caption.endPts - timelineStartPts;
  3099. caption.endTime /= 90e3;
  3100. event.captionStreams[caption.stream] = true;
  3101. event.captions.push(caption);
  3102. }
  3103. // Translate ID3 frame PTS times into second offsets into the
  3104. // video timeline for the segment
  3105. for (i = 0; i < this.pendingMetadata.length; i++) {
  3106. id3 = this.pendingMetadata[i];
  3107. id3.cueTime = id3.pts - timelineStartPts;
  3108. id3.cueTime /= 90e3;
  3109. event.metadata.push(id3);
  3110. }
  3111. // We add this to every single emitted segment even though we only need
  3112. // it for the first
  3113. event.metadata.dispatchType = this.metadataStream.dispatchType;
  3114. // Reset stream state
  3115. this.videoTrack = null;
  3116. this.audioTrack = null;
  3117. this.videoTags = [];
  3118. this.audioTags = [];
  3119. this.pendingCaptions.length = 0;
  3120. this.pendingMetadata.length = 0;
  3121. this.pendingTracks = 0;
  3122. this.processedTracks = 0;
  3123. // Emit the final segment
  3124. this.trigger('data', event);
  3125. this.trigger('done');
  3126. };
  3127. module.exports = CoalesceStream;
  3128. },{"../utils/stream.js":35}],20:[function(require,module,exports){
  3129. 'use strict';
  3130. var FlvTag = require('./flv-tag.js');
  3131. // For information on the FLV format, see
  3132. // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  3133. // Technically, this function returns the header and a metadata FLV tag
  3134. // if duration is greater than zero
  3135. // duration in seconds
  3136. // @return {object} the bytes of the FLV header as a Uint8Array
  3137. var getFlvHeader = function(duration, audio, video) { // :ByteArray {
  3138. var
  3139. headBytes = new Uint8Array(3 + 1 + 1 + 4),
  3140. head = new DataView(headBytes.buffer),
  3141. metadata,
  3142. result,
  3143. metadataLength;
  3144. // default arguments
  3145. duration = duration || 0;
  3146. audio = audio === undefined ? true : audio;
  3147. video = video === undefined ? true : video;
  3148. // signature
  3149. head.setUint8(0, 0x46); // 'F'
  3150. head.setUint8(1, 0x4c); // 'L'
  3151. head.setUint8(2, 0x56); // 'V'
  3152. // version
  3153. head.setUint8(3, 0x01);
  3154. // flags
  3155. head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
  3156. // data offset, should be 9 for FLV v1
  3157. head.setUint32(5, headBytes.byteLength);
  3158. // init the first FLV tag
  3159. if (duration <= 0) {
  3160. // no duration available so just write the first field of the first
  3161. // FLV tag
  3162. result = new Uint8Array(headBytes.byteLength + 4);
  3163. result.set(headBytes);
  3164. result.set([0, 0, 0, 0], headBytes.byteLength);
  3165. return result;
  3166. }
  3167. // write out the duration metadata tag
  3168. metadata = new FlvTag(FlvTag.METADATA_TAG);
  3169. metadata.pts = metadata.dts = 0;
  3170. metadata.writeMetaDataDouble('duration', duration);
  3171. metadataLength = metadata.finalize().length;
  3172. result = new Uint8Array(headBytes.byteLength + metadataLength);
  3173. result.set(headBytes);
  3174. result.set(head.byteLength, metadataLength);
  3175. return result;
  3176. };
  3177. module.exports = getFlvHeader;
  3178. },{"./flv-tag.js":21}],21:[function(require,module,exports){
  3179. /**
  3180. * An object that stores the bytes of an FLV tag and methods for
  3181. * querying and manipulating that data.
  3182. * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
  3183. */
  3184. 'use strict';
  3185. var FlvTag;
  3186. // (type:uint, extraData:Boolean = false) extends ByteArray
  3187. FlvTag = function(type, extraData) {
  3188. var
  3189. // Counter if this is a metadata tag, nal start marker if this is a video
  3190. // tag. unused if this is an audio tag
  3191. adHoc = 0, // :uint
  3192. // The default size is 16kb but this is not enough to hold iframe
  3193. // data and the resizing algorithm costs a bit so we create a larger
  3194. // starting buffer for video tags
  3195. bufferStartSize = 16384,
  3196. // checks whether the FLV tag has enough capacity to accept the proposed
  3197. // write and re-allocates the internal buffers if necessary
  3198. prepareWrite = function(flv, count) {
  3199. var
  3200. bytes,
  3201. minLength = flv.position + count;
  3202. if (minLength < flv.bytes.byteLength) {
  3203. // there's enough capacity so do nothing
  3204. return;
  3205. }
  3206. // allocate a new buffer and copy over the data that will not be modified
  3207. bytes = new Uint8Array(minLength * 2);
  3208. bytes.set(flv.bytes.subarray(0, flv.position), 0);
  3209. flv.bytes = bytes;
  3210. flv.view = new DataView(flv.bytes.buffer);
  3211. },
  3212. // commonly used metadata properties
  3213. widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
  3214. heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
  3215. videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
  3216. i;
  3217. if (!FlvTag.widthBytes) {
  3218. // calculating the bytes of common metadata names ahead of time makes the
  3219. // corresponding writes faster because we don't have to loop over the
  3220. // characters
  3221. // re-test with test/perf.html if you're planning on changing this
  3222. for (i = 0; i < 'width'.length; i++) {
  3223. widthBytes[i] = 'width'.charCodeAt(i);
  3224. }
  3225. for (i = 0; i < 'height'.length; i++) {
  3226. heightBytes[i] = 'height'.charCodeAt(i);
  3227. }
  3228. for (i = 0; i < 'videocodecid'.length; i++) {
  3229. videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
  3230. }
  3231. FlvTag.widthBytes = widthBytes;
  3232. FlvTag.heightBytes = heightBytes;
  3233. FlvTag.videocodecidBytes = videocodecidBytes;
  3234. }
  3235. this.keyFrame = false; // :Boolean
  3236. switch (type) {
  3237. case FlvTag.VIDEO_TAG:
  3238. this.length = 16;
  3239. // Start the buffer at 256k
  3240. bufferStartSize *= 6;
  3241. break;
  3242. case FlvTag.AUDIO_TAG:
  3243. this.length = 13;
  3244. this.keyFrame = true;
  3245. break;
  3246. case FlvTag.METADATA_TAG:
  3247. this.length = 29;
  3248. this.keyFrame = true;
  3249. break;
  3250. default:
  3251. throw new Error('Unknown FLV tag type');
  3252. }
  3253. this.bytes = new Uint8Array(bufferStartSize);
  3254. this.view = new DataView(this.bytes.buffer);
  3255. this.bytes[0] = type;
  3256. this.position = this.length;
  3257. this.keyFrame = extraData; // Defaults to false
  3258. // presentation timestamp
  3259. this.pts = 0;
  3260. // decoder timestamp
  3261. this.dts = 0;
  3262. // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  3263. this.writeBytes = function(bytes, offset, length) {
  3264. var
  3265. start = offset || 0,
  3266. end;
  3267. length = length || bytes.byteLength;
  3268. end = start + length;
  3269. prepareWrite(this, length);
  3270. this.bytes.set(bytes.subarray(start, end), this.position);
  3271. this.position += length;
  3272. this.length = Math.max(this.length, this.position);
  3273. };
  3274. // ByteArray#writeByte(value:int):void
  3275. this.writeByte = function(byte) {
  3276. prepareWrite(this, 1);
  3277. this.bytes[this.position] = byte;
  3278. this.position++;
  3279. this.length = Math.max(this.length, this.position);
  3280. };
  3281. // ByteArray#writeShort(value:int):void
  3282. this.writeShort = function(short) {
  3283. prepareWrite(this, 2);
  3284. this.view.setUint16(this.position, short);
  3285. this.position += 2;
  3286. this.length = Math.max(this.length, this.position);
  3287. };
  3288. // Negative index into array
  3289. // (pos:uint):int
  3290. this.negIndex = function(pos) {
  3291. return this.bytes[this.length - pos];
  3292. };
  3293. // The functions below ONLY work when this[0] == VIDEO_TAG.
  3294. // We are not going to check for that because we dont want the overhead
  3295. // (nal:ByteArray = null):int
  3296. this.nalUnitSize = function() {
  3297. if (adHoc === 0) {
  3298. return 0;
  3299. }
  3300. return this.length - (adHoc + 4);
  3301. };
  3302. this.startNalUnit = function() {
  3303. // remember position and add 4 bytes
  3304. if (adHoc > 0) {
  3305. throw new Error('Attempted to create new NAL wihout closing the old one');
  3306. }
  3307. // reserve 4 bytes for nal unit size
  3308. adHoc = this.length;
  3309. this.length += 4;
  3310. this.position = this.length;
  3311. };
  3312. // (nal:ByteArray = null):void
  3313. this.endNalUnit = function(nalContainer) {
  3314. var
  3315. nalStart, // :uint
  3316. nalLength; // :uint
  3317. // Rewind to the marker and write the size
  3318. if (this.length === adHoc + 4) {
  3319. // we started a nal unit, but didnt write one, so roll back the 4 byte size value
  3320. this.length -= 4;
  3321. } else if (adHoc > 0) {
  3322. nalStart = adHoc + 4;
  3323. nalLength = this.length - nalStart;
  3324. this.position = adHoc;
  3325. this.view.setUint32(this.position, nalLength);
  3326. this.position = this.length;
  3327. if (nalContainer) {
  3328. // Add the tag to the NAL unit
  3329. nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
  3330. }
  3331. }
  3332. adHoc = 0;
  3333. };
  3334. /**
  3335. * Write out a 64-bit floating point valued metadata property. This method is
  3336. * called frequently during a typical parse and needs to be fast.
  3337. */
  3338. // (key:String, val:Number):void
  3339. this.writeMetaDataDouble = function(key, val) {
  3340. var i;
  3341. prepareWrite(this, 2 + key.length + 9);
  3342. // write size of property name
  3343. this.view.setUint16(this.position, key.length);
  3344. this.position += 2;
  3345. // this next part looks terrible but it improves parser throughput by
  3346. // 10kB/s in my testing
  3347. // write property name
  3348. if (key === 'width') {
  3349. this.bytes.set(widthBytes, this.position);
  3350. this.position += 5;
  3351. } else if (key === 'height') {
  3352. this.bytes.set(heightBytes, this.position);
  3353. this.position += 6;
  3354. } else if (key === 'videocodecid') {
  3355. this.bytes.set(videocodecidBytes, this.position);
  3356. this.position += 12;
  3357. } else {
  3358. for (i = 0; i < key.length; i++) {
  3359. this.bytes[this.position] = key.charCodeAt(i);
  3360. this.position++;
  3361. }
  3362. }
  3363. // skip null byte
  3364. this.position++;
  3365. // write property value
  3366. this.view.setFloat64(this.position, val);
  3367. this.position += 8;
  3368. // update flv tag length
  3369. this.length = Math.max(this.length, this.position);
  3370. ++adHoc;
  3371. };
  3372. // (key:String, val:Boolean):void
  3373. this.writeMetaDataBoolean = function(key, val) {
  3374. var i;
  3375. prepareWrite(this, 2);
  3376. this.view.setUint16(this.position, key.length);
  3377. this.position += 2;
  3378. for (i = 0; i < key.length; i++) {
  3379. // if key.charCodeAt(i) >= 255, handle error
  3380. prepareWrite(this, 1);
  3381. this.bytes[this.position] = key.charCodeAt(i);
  3382. this.position++;
  3383. }
  3384. prepareWrite(this, 2);
  3385. this.view.setUint8(this.position, 0x01);
  3386. this.position++;
  3387. this.view.setUint8(this.position, val ? 0x01 : 0x00);
  3388. this.position++;
  3389. this.length = Math.max(this.length, this.position);
  3390. ++adHoc;
  3391. };
  3392. // ():ByteArray
  3393. this.finalize = function() {
  3394. var
  3395. dtsDelta, // :int
  3396. len; // :int
  3397. switch (this.bytes[0]) {
  3398. // Video Data
  3399. case FlvTag.VIDEO_TAG:
  3400. // We only support AVC, 1 = key frame (for AVC, a seekable
  3401. // frame), 2 = inter frame (for AVC, a non-seekable frame)
  3402. this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
  3403. this.bytes[12] = extraData ? 0x00 : 0x01;
  3404. dtsDelta = this.pts - this.dts;
  3405. this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
  3406. this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
  3407. this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
  3408. break;
  3409. case FlvTag.AUDIO_TAG:
  3410. this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
  3411. this.bytes[12] = extraData ? 0x00 : 0x01;
  3412. break;
  3413. case FlvTag.METADATA_TAG:
  3414. this.position = 11;
  3415. this.view.setUint8(this.position, 0x02); // String type
  3416. this.position++;
  3417. this.view.setUint16(this.position, 0x0A); // 10 Bytes
  3418. this.position += 2;
  3419. // set "onMetaData"
  3420. this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
  3421. 0x74, 0x61, 0x44, 0x61,
  3422. 0x74, 0x61], this.position);
  3423. this.position += 10;
  3424. this.bytes[this.position] = 0x08; // Array type
  3425. this.position++;
  3426. this.view.setUint32(this.position, adHoc);
  3427. this.position = this.length;
  3428. this.bytes.set([0, 0, 9], this.position);
  3429. this.position += 3; // End Data Tag
  3430. this.length = this.position;
  3431. break;
  3432. }
  3433. len = this.length - 11;
  3434. // write the DataSize field
  3435. this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
  3436. this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
  3437. this.bytes[ 3] = (len & 0x000000FF) >>> 0;
  3438. // write the Timestamp
  3439. this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
  3440. this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8;
  3441. this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0;
  3442. this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
  3443. // write the StreamID
  3444. this.bytes[ 8] = 0;
  3445. this.bytes[ 9] = 0;
  3446. this.bytes[10] = 0;
  3447. // Sometimes we're at the end of the view and have one slot to write a
  3448. // uint32, so, prepareWrite of count 4, since, view is uint8
  3449. prepareWrite(this, 4);
  3450. this.view.setUint32(this.length, this.length);
  3451. this.length += 4;
  3452. this.position += 4;
  3453. // trim down the byte buffer to what is actually being used
  3454. this.bytes = this.bytes.subarray(0, this.length);
  3455. this.frameTime = FlvTag.frameTime(this.bytes);
  3456. // if bytes.bytelength isn't equal to this.length, handle error
  3457. return this;
  3458. };
  3459. };
  3460. FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
  3461. FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
  3462. FlvTag.METADATA_TAG = 0x12; // == 18, :uint
  3463. // (tag:ByteArray):Boolean {
  3464. FlvTag.isAudioFrame = function(tag) {
  3465. return FlvTag.AUDIO_TAG === tag[0];
  3466. };
  3467. // (tag:ByteArray):Boolean {
  3468. FlvTag.isVideoFrame = function(tag) {
  3469. return FlvTag.VIDEO_TAG === tag[0];
  3470. };
  3471. // (tag:ByteArray):Boolean {
  3472. FlvTag.isMetaData = function(tag) {
  3473. return FlvTag.METADATA_TAG === tag[0];
  3474. };
  3475. // (tag:ByteArray):Boolean {
  3476. FlvTag.isKeyFrame = function(tag) {
  3477. if (FlvTag.isVideoFrame(tag)) {
  3478. return tag[11] === 0x17;
  3479. }
  3480. if (FlvTag.isAudioFrame(tag)) {
  3481. return true;
  3482. }
  3483. if (FlvTag.isMetaData(tag)) {
  3484. return true;
  3485. }
  3486. return false;
  3487. };
  3488. // (tag:ByteArray):uint {
  3489. FlvTag.frameTime = function(tag) {
  3490. var pts = tag[ 4] << 16; // :uint
  3491. pts |= tag[ 5] << 8;
  3492. pts |= tag[ 6] << 0;
  3493. pts |= tag[ 7] << 24;
  3494. return pts;
  3495. };
  3496. module.exports = FlvTag;
  3497. },{}],22:[function(require,module,exports){
  3498. module.exports = {
  3499. tag: require('./flv-tag'),
  3500. Transmuxer: require('./transmuxer'),
  3501. getFlvHeader: require('./flv-header')
  3502. };
  3503. },{"./flv-header":20,"./flv-tag":21,"./transmuxer":24}],23:[function(require,module,exports){
  3504. 'use strict';
  3505. var TagList = function() {
  3506. var self = this;
  3507. this.list = [];
  3508. this.push = function(tag) {
  3509. this.list.push({
  3510. bytes: tag.bytes,
  3511. dts: tag.dts,
  3512. pts: tag.pts,
  3513. keyFrame: tag.keyFrame,
  3514. metaDataTag: tag.metaDataTag
  3515. });
  3516. };
  3517. Object.defineProperty(this, 'length', {
  3518. get: function() {
  3519. return self.list.length;
  3520. }
  3521. });
  3522. };
  3523. module.exports = TagList;
  3524. },{}],24:[function(require,module,exports){
  3525. 'use strict';
  3526. var Stream = require('../utils/stream.js');
  3527. var FlvTag = require('./flv-tag.js');
  3528. var m2ts = require('../m2ts/m2ts.js');
  3529. var AdtsStream = require('../codecs/adts.js');
  3530. var H264Stream = require('../codecs/h264').H264Stream;
  3531. var CoalesceStream = require('./coalesce-stream.js');
  3532. var TagList = require('./tag-list.js');
  3533. var
  3534. Transmuxer,
  3535. VideoSegmentStream,
  3536. AudioSegmentStream,
  3537. collectTimelineInfo,
  3538. metaDataTag,
  3539. extraDataTag;
  3540. /**
  3541. * Store information about the start and end of the tracka and the
  3542. * duration for each frame/sample we process in order to calculate
  3543. * the baseMediaDecodeTime
  3544. */
  3545. collectTimelineInfo = function(track, data) {
  3546. if (typeof data.pts === 'number') {
  3547. if (track.timelineStartInfo.pts === undefined) {
  3548. track.timelineStartInfo.pts = data.pts;
  3549. } else {
  3550. track.timelineStartInfo.pts =
  3551. Math.min(track.timelineStartInfo.pts, data.pts);
  3552. }
  3553. }
  3554. if (typeof data.dts === 'number') {
  3555. if (track.timelineStartInfo.dts === undefined) {
  3556. track.timelineStartInfo.dts = data.dts;
  3557. } else {
  3558. track.timelineStartInfo.dts =
  3559. Math.min(track.timelineStartInfo.dts, data.dts);
  3560. }
  3561. }
  3562. };
  3563. metaDataTag = function(track, pts) {
  3564. var
  3565. tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
  3566. tag.dts = pts;
  3567. tag.pts = pts;
  3568. tag.writeMetaDataDouble('videocodecid', 7);
  3569. tag.writeMetaDataDouble('width', track.width);
  3570. tag.writeMetaDataDouble('height', track.height);
  3571. return tag;
  3572. };
  3573. extraDataTag = function(track, pts) {
  3574. var
  3575. i,
  3576. tag = new FlvTag(FlvTag.VIDEO_TAG, true);
  3577. tag.dts = pts;
  3578. tag.pts = pts;
  3579. tag.writeByte(0x01);// version
  3580. tag.writeByte(track.profileIdc);// profile
  3581. tag.writeByte(track.profileCompatibility);// compatibility
  3582. tag.writeByte(track.levelIdc);// level
  3583. tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  3584. tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
  3585. tag.writeShort(track.sps[0].length); // data of SPS
  3586. tag.writeBytes(track.sps[0]); // SPS
  3587. tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  3588. for (i = 0; i < track.pps.length; ++i) {
  3589. tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
  3590. tag.writeBytes(track.pps[i]); // data of PPS
  3591. }
  3592. return tag;
  3593. };
  3594. /**
  3595. * Constructs a single-track, media segment from AAC data
  3596. * events. The output of this stream can be fed to flash.
  3597. */
  3598. AudioSegmentStream = function(track) {
  3599. var
  3600. adtsFrames = [],
  3601. videoKeyFrames = [],
  3602. oldExtraData;
  3603. AudioSegmentStream.prototype.init.call(this);
  3604. this.push = function(data) {
  3605. collectTimelineInfo(track, data);
  3606. if (track) {
  3607. track.audioobjecttype = data.audioobjecttype;
  3608. track.channelcount = data.channelcount;
  3609. track.samplerate = data.samplerate;
  3610. track.samplingfrequencyindex = data.samplingfrequencyindex;
  3611. track.samplesize = data.samplesize;
  3612. track.extraData = (track.audioobjecttype << 11) |
  3613. (track.samplingfrequencyindex << 7) |
  3614. (track.channelcount << 3);
  3615. }
  3616. data.pts = Math.round(data.pts / 90);
  3617. data.dts = Math.round(data.dts / 90);
  3618. // buffer audio data until end() is called
  3619. adtsFrames.push(data);
  3620. };
  3621. this.flush = function() {
  3622. var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
  3623. // return early if no audio data has been observed
  3624. if (adtsFrames.length === 0) {
  3625. this.trigger('done', 'AudioSegmentStream');
  3626. return;
  3627. }
  3628. lastMetaPts = -Infinity;
  3629. while (adtsFrames.length) {
  3630. currentFrame = adtsFrames.shift();
  3631. // write out a metadata frame at every video key frame
  3632. if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) {
  3633. lastMetaPts = videoKeyFrames.shift();
  3634. this.writeMetaDataTags(tags, lastMetaPts);
  3635. }
  3636. // also write out metadata tags every 1 second so that the decoder
  3637. // is re-initialized quickly after seeking into a different
  3638. // audio configuration.
  3639. if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
  3640. this.writeMetaDataTags(tags, currentFrame.pts);
  3641. oldExtraData = track.extraData;
  3642. lastMetaPts = currentFrame.pts;
  3643. }
  3644. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
  3645. adtsFrame.pts = currentFrame.pts;
  3646. adtsFrame.dts = currentFrame.dts;
  3647. adtsFrame.writeBytes(currentFrame.data);
  3648. tags.push(adtsFrame.finalize());
  3649. }
  3650. videoKeyFrames.length = 0;
  3651. oldExtraData = null;
  3652. this.trigger('data', {track: track, tags: tags.list});
  3653. this.trigger('done', 'AudioSegmentStream');
  3654. };
  3655. this.writeMetaDataTags = function(tags, pts) {
  3656. var adtsFrame;
  3657. adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
  3658. // For audio, DTS is always the same as PTS. We want to set the DTS
  3659. // however so we can compare with video DTS to determine approximate
  3660. // packet order
  3661. adtsFrame.pts = pts;
  3662. adtsFrame.dts = pts;
  3663. // AAC is always 10
  3664. adtsFrame.writeMetaDataDouble('audiocodecid', 10);
  3665. adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
  3666. adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
  3667. // Is AAC always 16 bit?
  3668. adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
  3669. tags.push(adtsFrame.finalize());
  3670. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
  3671. // For audio, DTS is always the same as PTS. We want to set the DTS
  3672. // however so we can compare with video DTS to determine approximate
  3673. // packet order
  3674. adtsFrame.pts = pts;
  3675. adtsFrame.dts = pts;
  3676. adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
  3677. adtsFrame.position += 2;
  3678. adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
  3679. tags.push(adtsFrame.finalize());
  3680. };
  3681. this.onVideoKeyFrame = function(pts) {
  3682. videoKeyFrames.push(pts);
  3683. };
  3684. };
  3685. AudioSegmentStream.prototype = new Stream();
  3686. /**
  3687. * Store FlvTags for the h264 stream
  3688. * @param track {object} track metadata configuration
  3689. */
  3690. VideoSegmentStream = function(track) {
  3691. var
  3692. nalUnits = [],
  3693. config,
  3694. h264Frame;
  3695. VideoSegmentStream.prototype.init.call(this);
  3696. this.finishFrame = function(tags, frame) {
  3697. if (!frame) {
  3698. return;
  3699. }
  3700. // Check if keyframe and the length of tags.
  3701. // This makes sure we write metadata on the first frame of a segment.
  3702. if (config && track && track.newMetadata &&
  3703. (frame.keyFrame || tags.length === 0)) {
  3704. // Push extra data on every IDR frame in case we did a stream change + seek
  3705. var metaTag = metaDataTag(config, frame.dts).finalize();
  3706. var extraTag = extraDataTag(track, frame.dts).finalize();
  3707. metaTag.metaDataTag = extraTag.metaDataTag = true;
  3708. tags.push(metaTag);
  3709. tags.push(extraTag);
  3710. track.newMetadata = false;
  3711. this.trigger('keyframe', frame.dts);
  3712. }
  3713. frame.endNalUnit();
  3714. tags.push(frame.finalize());
  3715. h264Frame = null;
  3716. };
  3717. this.push = function(data) {
  3718. collectTimelineInfo(track, data);
  3719. data.pts = Math.round(data.pts / 90);
  3720. data.dts = Math.round(data.dts / 90);
  3721. // buffer video until flush() is called
  3722. nalUnits.push(data);
  3723. };
  3724. this.flush = function() {
  3725. var
  3726. currentNal,
  3727. tags = new TagList();
  3728. // Throw away nalUnits at the start of the byte stream until we find
  3729. // the first AUD
  3730. while (nalUnits.length) {
  3731. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  3732. break;
  3733. }
  3734. nalUnits.shift();
  3735. }
  3736. // return early if no video data has been observed
  3737. if (nalUnits.length === 0) {
  3738. this.trigger('done', 'VideoSegmentStream');
  3739. return;
  3740. }
  3741. while (nalUnits.length) {
  3742. currentNal = nalUnits.shift();
  3743. // record the track config
  3744. if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
  3745. track.newMetadata = true;
  3746. config = currentNal.config;
  3747. track.width = config.width;
  3748. track.height = config.height;
  3749. track.sps = [currentNal.data];
  3750. track.profileIdc = config.profileIdc;
  3751. track.levelIdc = config.levelIdc;
  3752. track.profileCompatibility = config.profileCompatibility;
  3753. h264Frame.endNalUnit();
  3754. } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
  3755. track.newMetadata = true;
  3756. track.pps = [currentNal.data];
  3757. h264Frame.endNalUnit();
  3758. } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  3759. if (h264Frame) {
  3760. this.finishFrame(tags, h264Frame);
  3761. }
  3762. h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
  3763. h264Frame.pts = currentNal.pts;
  3764. h264Frame.dts = currentNal.dts;
  3765. } else {
  3766. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  3767. // the current sample is a key frame
  3768. h264Frame.keyFrame = true;
  3769. }
  3770. h264Frame.endNalUnit();
  3771. }
  3772. h264Frame.startNalUnit();
  3773. h264Frame.writeBytes(currentNal.data);
  3774. }
  3775. if (h264Frame) {
  3776. this.finishFrame(tags, h264Frame);
  3777. }
  3778. this.trigger('data', {track: track, tags: tags.list});
  3779. // Continue with the flush process now
  3780. this.trigger('done', 'VideoSegmentStream');
  3781. };
  3782. };
  3783. VideoSegmentStream.prototype = new Stream();
  3784. /**
  3785. * An object that incrementally transmuxes MPEG2 Trasport Stream
  3786. * chunks into an FLV.
  3787. */
  3788. Transmuxer = function(options) {
  3789. var
  3790. self = this,
  3791. packetStream, parseStream, elementaryStream,
  3792. videoTimestampRolloverStream, audioTimestampRolloverStream,
  3793. timedMetadataTimestampRolloverStream,
  3794. adtsStream, h264Stream,
  3795. videoSegmentStream, audioSegmentStream, captionStream,
  3796. coalesceStream;
  3797. Transmuxer.prototype.init.call(this);
  3798. options = options || {};
  3799. // expose the metadata stream
  3800. this.metadataStream = new m2ts.MetadataStream();
  3801. options.metadataStream = this.metadataStream;
  3802. // set up the parsing pipeline
  3803. packetStream = new m2ts.TransportPacketStream();
  3804. parseStream = new m2ts.TransportParseStream();
  3805. elementaryStream = new m2ts.ElementaryStream();
  3806. videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  3807. audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  3808. timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  3809. adtsStream = new AdtsStream();
  3810. h264Stream = new H264Stream();
  3811. coalesceStream = new CoalesceStream(options);
  3812. // disassemble MPEG2-TS packets into elementary streams
  3813. packetStream
  3814. .pipe(parseStream)
  3815. .pipe(elementaryStream);
  3816. // !!THIS ORDER IS IMPORTANT!!
  3817. // demux the streams
  3818. elementaryStream
  3819. .pipe(videoTimestampRolloverStream)
  3820. .pipe(h264Stream);
  3821. elementaryStream
  3822. .pipe(audioTimestampRolloverStream)
  3823. .pipe(adtsStream);
  3824. elementaryStream
  3825. .pipe(timedMetadataTimestampRolloverStream)
  3826. .pipe(this.metadataStream)
  3827. .pipe(coalesceStream);
  3828. // if CEA-708 parsing is available, hook up a caption stream
  3829. captionStream = new m2ts.CaptionStream();
  3830. h264Stream.pipe(captionStream)
  3831. .pipe(coalesceStream);
  3832. // hook up the segment streams once track metadata is delivered
  3833. elementaryStream.on('data', function(data) {
  3834. var i, videoTrack, audioTrack;
  3835. if (data.type === 'metadata') {
  3836. i = data.tracks.length;
  3837. // scan the tracks listed in the metadata
  3838. while (i--) {
  3839. if (data.tracks[i].type === 'video') {
  3840. videoTrack = data.tracks[i];
  3841. } else if (data.tracks[i].type === 'audio') {
  3842. audioTrack = data.tracks[i];
  3843. }
  3844. }
  3845. // hook up the video segment stream to the first track with h264 data
  3846. if (videoTrack && !videoSegmentStream) {
  3847. coalesceStream.numberOfTracks++;
  3848. videoSegmentStream = new VideoSegmentStream(videoTrack);
  3849. // Set up the final part of the video pipeline
  3850. h264Stream
  3851. .pipe(videoSegmentStream)
  3852. .pipe(coalesceStream);
  3853. }
  3854. if (audioTrack && !audioSegmentStream) {
  3855. // hook up the audio segment stream to the first track with aac data
  3856. coalesceStream.numberOfTracks++;
  3857. audioSegmentStream = new AudioSegmentStream(audioTrack);
  3858. // Set up the final part of the audio pipeline
  3859. adtsStream
  3860. .pipe(audioSegmentStream)
  3861. .pipe(coalesceStream);
  3862. if (videoSegmentStream) {
  3863. videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame);
  3864. }
  3865. }
  3866. }
  3867. });
  3868. // feed incoming data to the front of the parsing pipeline
  3869. this.push = function(data) {
  3870. packetStream.push(data);
  3871. };
  3872. // flush any buffered data
  3873. this.flush = function() {
  3874. // Start at the top of the pipeline and flush all pending work
  3875. packetStream.flush();
  3876. };
  3877. // Caption data has to be reset when seeking outside buffered range
  3878. this.resetCaptions = function() {
  3879. captionStream.reset();
  3880. };
  3881. // Re-emit any data coming from the coalesce stream to the outside world
  3882. coalesceStream.on('data', function(event) {
  3883. self.trigger('data', event);
  3884. });
  3885. // Let the consumer know we have finished flushing the entire pipeline
  3886. coalesceStream.on('done', function() {
  3887. self.trigger('done');
  3888. });
  3889. };
  3890. Transmuxer.prototype = new Stream();
  3891. // forward compatibility
  3892. module.exports = Transmuxer;
  3893. },{"../codecs/adts.js":16,"../codecs/h264":17,"../m2ts/m2ts.js":26,"../utils/stream.js":35,"./coalesce-stream.js":19,"./flv-tag.js":21,"./tag-list.js":23}],25:[function(require,module,exports){
  3894. /**
  3895. * mux.js
  3896. *
  3897. * Copyright (c) 2015 Brightcove
  3898. * All rights reserved.
  3899. *
  3900. * Reads in-band caption information from a video elementary
  3901. * stream. Captions must follow the CEA-708 standard for injection
  3902. * into an MPEG-2 transport streams.
  3903. * @see https://en.wikipedia.org/wiki/CEA-708
  3904. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  3905. */
  3906. 'use strict';
  3907. // -----------------
  3908. // Link To Transport
  3909. // -----------------
  3910. // Supplemental enhancement information (SEI) NAL units have a
  3911. // payload type field to indicate how they are to be
  3912. // interpreted. CEAS-708 caption content is always transmitted with
  3913. // payload type 0x04.
  3914. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  3915. RBSP_TRAILING_BITS = 128,
  3916. Stream = require('../utils/stream');
  3917. /**
  3918. * Parse a supplemental enhancement information (SEI) NAL unit.
  3919. * Stops parsing once a message of type ITU T T35 has been found.
  3920. *
  3921. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  3922. * @return {object} the parsed SEI payload
  3923. * @see Rec. ITU-T H.264, 7.3.2.3.1
  3924. */
  3925. var parseSei = function(bytes) {
  3926. var
  3927. i = 0,
  3928. result = {
  3929. payloadType: -1,
  3930. payloadSize: 0
  3931. },
  3932. payloadType = 0,
  3933. payloadSize = 0;
  3934. // go through the sei_rbsp parsing each each individual sei_message
  3935. while (i < bytes.byteLength) {
  3936. // stop once we have hit the end of the sei_rbsp
  3937. if (bytes[i] === RBSP_TRAILING_BITS) {
  3938. break;
  3939. }
  3940. // Parse payload type
  3941. while (bytes[i] === 0xFF) {
  3942. payloadType += 255;
  3943. i++;
  3944. }
  3945. payloadType += bytes[i++];
  3946. // Parse payload size
  3947. while (bytes[i] === 0xFF) {
  3948. payloadSize += 255;
  3949. i++;
  3950. }
  3951. payloadSize += bytes[i++];
  3952. // this sei_message is a 608/708 caption so save it and break
  3953. // there can only ever be one caption message in a frame's sei
  3954. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  3955. result.payloadType = payloadType;
  3956. result.payloadSize = payloadSize;
  3957. result.payload = bytes.subarray(i, i + payloadSize);
  3958. break;
  3959. }
  3960. // skip the payload and parse the next message
  3961. i += payloadSize;
  3962. payloadType = 0;
  3963. payloadSize = 0;
  3964. }
  3965. return result;
  3966. };
  3967. // see ANSI/SCTE 128-1 (2013), section 8.1
  3968. var parseUserData = function(sei) {
  3969. // itu_t_t35_contry_code must be 181 (United States) for
  3970. // captions
  3971. if (sei.payload[0] !== 181) {
  3972. return null;
  3973. }
  3974. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  3975. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  3976. return null;
  3977. }
  3978. // the user_identifier should be "GA94" to indicate ATSC1 data
  3979. if (String.fromCharCode(sei.payload[3],
  3980. sei.payload[4],
  3981. sei.payload[5],
  3982. sei.payload[6]) !== 'GA94') {
  3983. return null;
  3984. }
  3985. // finally, user_data_type_code should be 0x03 for caption data
  3986. if (sei.payload[7] !== 0x03) {
  3987. return null;
  3988. }
  3989. // return the user_data_type_structure and strip the trailing
  3990. // marker bits
  3991. return sei.payload.subarray(8, sei.payload.length - 1);
  3992. };
  3993. // see CEA-708-D, section 4.4
  3994. var parseCaptionPackets = function(pts, userData) {
  3995. var results = [], i, count, offset, data;
  3996. // if this is just filler, return immediately
  3997. if (!(userData[0] & 0x40)) {
  3998. return results;
  3999. }
  4000. // parse out the cc_data_1 and cc_data_2 fields
  4001. count = userData[0] & 0x1f;
  4002. for (i = 0; i < count; i++) {
  4003. offset = i * 3;
  4004. data = {
  4005. type: userData[offset + 2] & 0x03,
  4006. pts: pts
  4007. };
  4008. // capture cc data when cc_valid is 1
  4009. if (userData[offset + 2] & 0x04) {
  4010. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  4011. results.push(data);
  4012. }
  4013. }
  4014. return results;
  4015. };
  4016. var CaptionStream = function() {
  4017. CaptionStream.prototype.init.call(this);
  4018. this.captionPackets_ = [];
  4019. this.ccStreams_ = [
  4020. new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
  4021. new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
  4022. new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
  4023. new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
  4024. ];
  4025. this.reset();
  4026. // forward data and done events from CCs to this CaptionStream
  4027. this.ccStreams_.forEach(function(cc) {
  4028. cc.on('data', this.trigger.bind(this, 'data'));
  4029. cc.on('done', this.trigger.bind(this, 'done'));
  4030. }, this);
  4031. };
  4032. CaptionStream.prototype = new Stream();
  4033. CaptionStream.prototype.push = function(event) {
  4034. var sei, userData;
  4035. // only examine SEI NALs
  4036. if (event.nalUnitType !== 'sei_rbsp') {
  4037. return;
  4038. }
  4039. // parse the sei
  4040. sei = parseSei(event.escapedRBSP);
  4041. // ignore everything but user_data_registered_itu_t_t35
  4042. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  4043. return;
  4044. }
  4045. // parse out the user data payload
  4046. userData = parseUserData(sei);
  4047. // ignore unrecognized userData
  4048. if (!userData) {
  4049. return;
  4050. }
  4051. // Sometimes, the same segment # will be downloaded twice. To stop the
  4052. // caption data from being processed twice, we track the latest dts we've
  4053. // received and ignore everything with a dts before that. However, since
  4054. // data for a specific dts can be split across 2 packets on either side of
  4055. // a segment boundary, we need to make sure we *don't* ignore the second
  4056. // dts packet we receive that has dts === this.latestDts_. And thus, the
  4057. // ignoreNextEqualDts_ flag was born.
  4058. if (event.dts < this.latestDts_) {
  4059. // We've started getting older data, so set the flag.
  4060. this.ignoreNextEqualDts_ = true;
  4061. return;
  4062. } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
  4063. // We've received the last duplicate packet, time to start processing again
  4064. this.ignoreNextEqualDts_ = false;
  4065. return;
  4066. }
  4067. // parse out CC data packets and save them for later
  4068. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  4069. this.latestDts_ = event.dts;
  4070. };
  4071. CaptionStream.prototype.flush = function() {
  4072. // make sure we actually parsed captions before proceeding
  4073. if (!this.captionPackets_.length) {
  4074. this.ccStreams_.forEach(function(cc) {
  4075. cc.flush();
  4076. }, this);
  4077. return;
  4078. }
  4079. // In Chrome, the Array#sort function is not stable so add a
  4080. // presortIndex that we can use to ensure we get a stable-sort
  4081. this.captionPackets_.forEach(function(elem, idx) {
  4082. elem.presortIndex = idx;
  4083. });
  4084. // sort caption byte-pairs based on their PTS values
  4085. this.captionPackets_.sort(function(a, b) {
  4086. if (a.pts === b.pts) {
  4087. return a.presortIndex - b.presortIndex;
  4088. }
  4089. return a.pts - b.pts;
  4090. });
  4091. this.captionPackets_.forEach(function(packet) {
  4092. if (packet.type < 2) {
  4093. // Dispatch packet to the right Cea608Stream
  4094. this.dispatchCea608Packet(packet);
  4095. }
  4096. // this is where an 'else' would go for a dispatching packets
  4097. // to a theoretical Cea708Stream that handles SERVICEn data
  4098. }, this);
  4099. this.captionPackets_.length = 0;
  4100. this.ccStreams_.forEach(function(cc) {
  4101. cc.flush();
  4102. }, this);
  4103. return;
  4104. };
  4105. CaptionStream.prototype.reset = function() {
  4106. this.latestDts_ = null;
  4107. this.ignoreNextEqualDts_ = false;
  4108. this.activeCea608Channel_ = [null, null];
  4109. this.ccStreams_.forEach(function(ccStream) {
  4110. ccStream.reset();
  4111. });
  4112. };
  4113. CaptionStream.prototype.dispatchCea608Packet = function(packet) {
  4114. // NOTE: packet.type is the CEA608 field
  4115. if (this.setsChannel1Active(packet)) {
  4116. this.activeCea608Channel_[packet.type] = 0;
  4117. } else if (this.setsChannel2Active(packet)) {
  4118. this.activeCea608Channel_[packet.type] = 1;
  4119. }
  4120. if (this.activeCea608Channel_[packet.type] === null) {
  4121. // If we haven't received anything to set the active channel, discard the
  4122. // data; we don't want jumbled captions
  4123. return;
  4124. }
  4125. this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
  4126. };
  4127. CaptionStream.prototype.setsChannel1Active = function(packet) {
  4128. return ((packet.ccData & 0x7800) === 0x1000);
  4129. };
  4130. CaptionStream.prototype.setsChannel2Active = function(packet) {
  4131. return ((packet.ccData & 0x7800) === 0x1800);
  4132. };
  4133. // ----------------------
  4134. // Session to Application
  4135. // ----------------------
  4136. var CHARACTER_TRANSLATION = {
  4137. 0x2a: 0xe1, // á
  4138. 0x5c: 0xe9, // é
  4139. 0x5e: 0xed, // í
  4140. 0x5f: 0xf3, // ó
  4141. 0x60: 0xfa, // ú
  4142. 0x7b: 0xe7, // ç
  4143. 0x7c: 0xf7, // ÷
  4144. 0x7d: 0xd1, // Ñ
  4145. 0x7e: 0xf1, // ñ
  4146. 0x7f: 0x2588, // █
  4147. 0x0130: 0xae, // ®
  4148. 0x0131: 0xb0, // °
  4149. 0x0132: 0xbd, // ½
  4150. 0x0133: 0xbf, // ¿
  4151. 0x0134: 0x2122, // ™
  4152. 0x0135: 0xa2, // ¢
  4153. 0x0136: 0xa3, // £
  4154. 0x0137: 0x266a, // ♪
  4155. 0x0138: 0xe0, // à
  4156. 0x0139: 0xa0, //
  4157. 0x013a: 0xe8, // è
  4158. 0x013b: 0xe2, // â
  4159. 0x013c: 0xea, // ê
  4160. 0x013d: 0xee, // î
  4161. 0x013e: 0xf4, // ô
  4162. 0x013f: 0xfb, // û
  4163. 0x0220: 0xc1, // Á
  4164. 0x0221: 0xc9, // É
  4165. 0x0222: 0xd3, // Ó
  4166. 0x0223: 0xda, // Ú
  4167. 0x0224: 0xdc, // Ü
  4168. 0x0225: 0xfc, // ü
  4169. 0x0226: 0x2018, // ‘
  4170. 0x0227: 0xa1, // ¡
  4171. 0x0228: 0x2a, // *
  4172. 0x0229: 0x27, // '
  4173. 0x022a: 0x2014, // —
  4174. 0x022b: 0xa9, // ©
  4175. 0x022c: 0x2120, // ℠
  4176. 0x022d: 0x2022, // •
  4177. 0x022e: 0x201c, // “
  4178. 0x022f: 0x201d, // ”
  4179. 0x0230: 0xc0, // À
  4180. 0x0231: 0xc2, // Â
  4181. 0x0232: 0xc7, // Ç
  4182. 0x0233: 0xc8, // È
  4183. 0x0234: 0xca, // Ê
  4184. 0x0235: 0xcb, // Ë
  4185. 0x0236: 0xeb, // ë
  4186. 0x0237: 0xce, // Î
  4187. 0x0238: 0xcf, // Ï
  4188. 0x0239: 0xef, // ï
  4189. 0x023a: 0xd4, // Ô
  4190. 0x023b: 0xd9, // Ù
  4191. 0x023c: 0xf9, // ù
  4192. 0x023d: 0xdb, // Û
  4193. 0x023e: 0xab, // «
  4194. 0x023f: 0xbb, // »
  4195. 0x0320: 0xc3, // Ã
  4196. 0x0321: 0xe3, // ã
  4197. 0x0322: 0xcd, // Í
  4198. 0x0323: 0xcc, // Ì
  4199. 0x0324: 0xec, // ì
  4200. 0x0325: 0xd2, // Ò
  4201. 0x0326: 0xf2, // ò
  4202. 0x0327: 0xd5, // Õ
  4203. 0x0328: 0xf5, // õ
  4204. 0x0329: 0x7b, // {
  4205. 0x032a: 0x7d, // }
  4206. 0x032b: 0x5c, // \
  4207. 0x032c: 0x5e, // ^
  4208. 0x032d: 0x5f, // _
  4209. 0x032e: 0x7c, // |
  4210. 0x032f: 0x7e, // ~
  4211. 0x0330: 0xc4, // Ä
  4212. 0x0331: 0xe4, // ä
  4213. 0x0332: 0xd6, // Ö
  4214. 0x0333: 0xf6, // ö
  4215. 0x0334: 0xdf, // ß
  4216. 0x0335: 0xa5, // ¥
  4217. 0x0336: 0xa4, // ¤
  4218. 0x0337: 0x2502, // │
  4219. 0x0338: 0xc5, // Å
  4220. 0x0339: 0xe5, // å
  4221. 0x033a: 0xd8, // Ø
  4222. 0x033b: 0xf8, // ø
  4223. 0x033c: 0x250c, // ┌
  4224. 0x033d: 0x2510, // ┐
  4225. 0x033e: 0x2514, // └
  4226. 0x033f: 0x2518 // ┘
  4227. };
  4228. var getCharFromCode = function(code) {
  4229. if (code === null) {
  4230. return '';
  4231. }
  4232. code = CHARACTER_TRANSLATION[code] || code;
  4233. return String.fromCharCode(code);
  4234. };
  4235. // the index of the last row in a CEA-608 display buffer
  4236. var BOTTOM_ROW = 14;
  4237. // This array is used for mapping PACs -> row #, since there's no way of
  4238. // getting it through bit logic.
  4239. var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
  4240. 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
  4241. // CEA-608 captions are rendered onto a 34x15 matrix of character
  4242. // cells. The "bottom" row is the last element in the outer array.
  4243. var createDisplayBuffer = function() {
  4244. var result = [], i = BOTTOM_ROW + 1;
  4245. while (i--) {
  4246. result.push('');
  4247. }
  4248. return result;
  4249. };
  4250. var Cea608Stream = function(field, dataChannel) {
  4251. Cea608Stream.prototype.init.call(this);
  4252. this.field_ = field || 0;
  4253. this.dataChannel_ = dataChannel || 0;
  4254. this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
  4255. this.setConstants();
  4256. this.reset();
  4257. this.push = function(packet) {
  4258. var data, swap, char0, char1, text;
  4259. // remove the parity bits
  4260. data = packet.ccData & 0x7f7f;
  4261. // ignore duplicate control codes; the spec demands they're sent twice
  4262. if (data === this.lastControlCode_) {
  4263. this.lastControlCode_ = null;
  4264. return;
  4265. }
  4266. // Store control codes
  4267. if ((data & 0xf000) === 0x1000) {
  4268. this.lastControlCode_ = data;
  4269. } else if (data !== this.PADDING_) {
  4270. this.lastControlCode_ = null;
  4271. }
  4272. char0 = data >>> 8;
  4273. char1 = data & 0xff;
  4274. if (data === this.PADDING_) {
  4275. return;
  4276. } else if (data === this.RESUME_CAPTION_LOADING_) {
  4277. this.mode_ = 'popOn';
  4278. } else if (data === this.END_OF_CAPTION_) {
  4279. this.clearFormatting(packet.pts);
  4280. // if a caption was being displayed, it's gone now
  4281. this.flushDisplayed(packet.pts);
  4282. // flip memory
  4283. swap = this.displayed_;
  4284. this.displayed_ = this.nonDisplayed_;
  4285. this.nonDisplayed_ = swap;
  4286. // start measuring the time to display the caption
  4287. this.startPts_ = packet.pts;
  4288. } else if (data === this.ROLL_UP_2_ROWS_) {
  4289. this.topRow_ = BOTTOM_ROW - 1;
  4290. this.mode_ = 'rollUp';
  4291. } else if (data === this.ROLL_UP_3_ROWS_) {
  4292. this.topRow_ = BOTTOM_ROW - 2;
  4293. this.mode_ = 'rollUp';
  4294. } else if (data === this.ROLL_UP_4_ROWS_) {
  4295. this.topRow_ = BOTTOM_ROW - 3;
  4296. this.mode_ = 'rollUp';
  4297. } else if (data === this.CARRIAGE_RETURN_) {
  4298. this.clearFormatting(packet.pts);
  4299. this.flushDisplayed(packet.pts);
  4300. this.shiftRowsUp_();
  4301. this.startPts_ = packet.pts;
  4302. } else if (data === this.BACKSPACE_) {
  4303. if (this.mode_ === 'popOn') {
  4304. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  4305. } else {
  4306. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  4307. }
  4308. } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
  4309. this.flushDisplayed(packet.pts);
  4310. this.displayed_ = createDisplayBuffer();
  4311. } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
  4312. this.nonDisplayed_ = createDisplayBuffer();
  4313. } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
  4314. this.mode_ = 'paintOn';
  4315. // Append special characters to caption text
  4316. } else if (this.isSpecialCharacter(char0, char1)) {
  4317. // Bitmask char0 so that we can apply character transformations
  4318. // regardless of field and data channel.
  4319. // Then byte-shift to the left and OR with char1 so we can pass the
  4320. // entire character code to `getCharFromCode`.
  4321. char0 = (char0 & 0x03) << 8;
  4322. text = getCharFromCode(char0 | char1);
  4323. this[this.mode_](packet.pts, text);
  4324. this.column_++;
  4325. // Append extended characters to caption text
  4326. } else if (this.isExtCharacter(char0, char1)) {
  4327. // Extended characters always follow their "non-extended" equivalents.
  4328. // IE if a "è" is desired, you'll always receive "eè"; non-compliant
  4329. // decoders are supposed to drop the "è", while compliant decoders
  4330. // backspace the "e" and insert "è".
  4331. // Delete the previous character
  4332. if (this.mode_ === 'popOn') {
  4333. this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
  4334. } else {
  4335. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  4336. }
  4337. // Bitmask char0 so that we can apply character transformations
  4338. // regardless of field and data channel.
  4339. // Then byte-shift to the left and OR with char1 so we can pass the
  4340. // entire character code to `getCharFromCode`.
  4341. char0 = (char0 & 0x03) << 8;
  4342. text = getCharFromCode(char0 | char1);
  4343. this[this.mode_](packet.pts, text);
  4344. this.column_++;
  4345. // Process mid-row codes
  4346. } else if (this.isMidRowCode(char0, char1)) {
  4347. // Attributes are not additive, so clear all formatting
  4348. this.clearFormatting(packet.pts);
  4349. // According to the standard, mid-row codes
  4350. // should be replaced with spaces, so add one now
  4351. this[this.mode_](packet.pts, ' ');
  4352. this.column_++;
  4353. if ((char1 & 0xe) === 0xe) {
  4354. this.addFormatting(packet.pts, ['i']);
  4355. }
  4356. if ((char1 & 0x1) === 0x1) {
  4357. this.addFormatting(packet.pts, ['u']);
  4358. }
  4359. // Detect offset control codes and adjust cursor
  4360. } else if (this.isOffsetControlCode(char0, char1)) {
  4361. // Cursor position is set by indent PAC (see below) in 4-column
  4362. // increments, with an additional offset code of 1-3 to reach any
  4363. // of the 32 columns specified by CEA-608. So all we need to do
  4364. // here is increment the column cursor by the given offset.
  4365. this.column_ += (char1 & 0x03);
  4366. // Detect PACs (Preamble Address Codes)
  4367. } else if (this.isPAC(char0, char1)) {
  4368. // There's no logic for PAC -> row mapping, so we have to just
  4369. // find the row code in an array and use its index :(
  4370. var row = ROWS.indexOf(data & 0x1f20);
  4371. if (row !== this.row_) {
  4372. // formatting is only persistent for current row
  4373. this.clearFormatting(packet.pts);
  4374. this.row_ = row;
  4375. }
  4376. // All PACs can apply underline, so detect and apply
  4377. // (All odd-numbered second bytes set underline)
  4378. if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
  4379. this.addFormatting(packet.pts, ['u']);
  4380. }
  4381. if ((data & 0x10) === 0x10) {
  4382. // We've got an indent level code. Each successive even number
  4383. // increments the column cursor by 4, so we can get the desired
  4384. // column position by bit-shifting to the right (to get n/2)
  4385. // and multiplying by 4.
  4386. this.column_ = ((data & 0xe) >> 1) * 4;
  4387. }
  4388. if (this.isColorPAC(char1)) {
  4389. // it's a color code, though we only support white, which
  4390. // can be either normal or italicized. white italics can be
  4391. // either 0x4e or 0x6e depending on the row, so we just
  4392. // bitwise-and with 0xe to see if italics should be turned on
  4393. if ((char1 & 0xe) === 0xe) {
  4394. this.addFormatting(packet.pts, ['i']);
  4395. }
  4396. }
  4397. // We have a normal character in char0, and possibly one in char1
  4398. } else if (this.isNormalChar(char0)) {
  4399. if (char1 === 0x00) {
  4400. char1 = null;
  4401. }
  4402. text = getCharFromCode(char0);
  4403. text += getCharFromCode(char1);
  4404. this[this.mode_](packet.pts, text);
  4405. this.column_ += text.length;
  4406. } // finish data processing
  4407. };
  4408. };
  4409. Cea608Stream.prototype = new Stream();
  4410. // Trigger a cue point that captures the current state of the
  4411. // display buffer
  4412. Cea608Stream.prototype.flushDisplayed = function(pts) {
  4413. var content = this.displayed_
  4414. // remove spaces from the start and end of the string
  4415. .map(function(row) {
  4416. return row.trim();
  4417. })
  4418. // combine all text rows to display in one cue
  4419. .join('\n')
  4420. // and remove blank rows from the start and end, but not the middle
  4421. .replace(/^\n+|\n+$/g, '');
  4422. if (content.length) {
  4423. this.trigger('data', {
  4424. startPts: this.startPts_,
  4425. endPts: pts,
  4426. text: content,
  4427. stream: this.name_
  4428. });
  4429. }
  4430. };
  4431. /**
  4432. * Zero out the data, used for startup and on seek
  4433. */
  4434. Cea608Stream.prototype.reset = function() {
  4435. this.mode_ = 'popOn';
  4436. // When in roll-up mode, the index of the last row that will
  4437. // actually display captions. If a caption is shifted to a row
  4438. // with a lower index than this, it is cleared from the display
  4439. // buffer
  4440. this.topRow_ = 0;
  4441. this.startPts_ = 0;
  4442. this.displayed_ = createDisplayBuffer();
  4443. this.nonDisplayed_ = createDisplayBuffer();
  4444. this.lastControlCode_ = null;
  4445. // Track row and column for proper line-breaking and spacing
  4446. this.column_ = 0;
  4447. this.row_ = BOTTOM_ROW;
  4448. // This variable holds currently-applied formatting
  4449. this.formatting_ = [];
  4450. };
  4451. /**
  4452. * Sets up control code and related constants for this instance
  4453. */
  4454. Cea608Stream.prototype.setConstants = function() {
  4455. // The following attributes have these uses:
  4456. // ext_ : char0 for mid-row codes, and the base for extended
  4457. // chars (ext_+0, ext_+1, and ext_+2 are char0s for
  4458. // extended codes)
  4459. // control_: char0 for control codes, except byte-shifted to the
  4460. // left so that we can do this.control_ | CONTROL_CODE
  4461. // offset_: char0 for tab offset codes
  4462. //
  4463. // It's also worth noting that control codes, and _only_ control codes,
  4464. // differ between field 1 and field2. Field 2 control codes are always
  4465. // their field 1 value plus 1. That's why there's the "| field" on the
  4466. // control value.
  4467. if (this.dataChannel_ === 0) {
  4468. this.BASE_ = 0x10;
  4469. this.EXT_ = 0x11;
  4470. this.CONTROL_ = (0x14 | this.field_) << 8;
  4471. this.OFFSET_ = 0x17;
  4472. } else if (this.dataChannel_ === 1) {
  4473. this.BASE_ = 0x18;
  4474. this.EXT_ = 0x19;
  4475. this.CONTROL_ = (0x1c | this.field_) << 8;
  4476. this.OFFSET_ = 0x1f;
  4477. }
  4478. // Constants for the LSByte command codes recognized by Cea608Stream. This
  4479. // list is not exhaustive. For a more comprehensive listing and semantics see
  4480. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  4481. // Padding
  4482. this.PADDING_ = 0x0000;
  4483. // Pop-on Mode
  4484. this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
  4485. this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
  4486. // Roll-up Mode
  4487. this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
  4488. this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
  4489. this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
  4490. this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
  4491. // paint-on mode (not supported)
  4492. this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
  4493. // Erasure
  4494. this.BACKSPACE_ = this.CONTROL_ | 0x21;
  4495. this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
  4496. this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
  4497. };
  4498. /**
  4499. * Detects if the 2-byte packet data is a special character
  4500. *
  4501. * Special characters have a second byte in the range 0x30 to 0x3f,
  4502. * with the first byte being 0x11 (for data channel 1) or 0x19 (for
  4503. * data channel 2).
  4504. *
  4505. * @param {Integer} char0 The first byte
  4506. * @param {Integer} char1 The second byte
  4507. * @return {Boolean} Whether the 2 bytes are an special character
  4508. */
  4509. Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
  4510. return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
  4511. };
  4512. /**
  4513. * Detects if the 2-byte packet data is an extended character
  4514. *
  4515. * Extended characters have a second byte in the range 0x20 to 0x3f,
  4516. * with the first byte being 0x12 or 0x13 (for data channel 1) or
  4517. * 0x1a or 0x1b (for data channel 2).
  4518. *
  4519. * @param {Integer} char0 The first byte
  4520. * @param {Integer} char1 The second byte
  4521. * @return {Boolean} Whether the 2 bytes are an extended character
  4522. */
  4523. Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
  4524. return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
  4525. (char1 >= 0x20 && char1 <= 0x3f));
  4526. };
  4527. /**
  4528. * Detects if the 2-byte packet is a mid-row code
  4529. *
  4530. * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
  4531. * the first byte being 0x11 (for data channel 1) or 0x19 (for data
  4532. * channel 2).
  4533. *
  4534. * @param {Integer} char0 The first byte
  4535. * @param {Integer} char1 The second byte
  4536. * @return {Boolean} Whether the 2 bytes are a mid-row code
  4537. */
  4538. Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
  4539. return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
  4540. };
  4541. /**
  4542. * Detects if the 2-byte packet is an offset control code
  4543. *
  4544. * Offset control codes have a second byte in the range 0x21 to 0x23,
  4545. * with the first byte being 0x17 (for data channel 1) or 0x1f (for
  4546. * data channel 2).
  4547. *
  4548. * @param {Integer} char0 The first byte
  4549. * @param {Integer} char1 The second byte
  4550. * @return {Boolean} Whether the 2 bytes are an offset control code
  4551. */
  4552. Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
  4553. return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
  4554. };
  4555. /**
  4556. * Detects if the 2-byte packet is a Preamble Address Code
  4557. *
  4558. * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
  4559. * or 0x18 to 0x1f (for data channel 2), with the second byte in the
  4560. * range 0x40 to 0x7f.
  4561. *
  4562. * @param {Integer} char0 The first byte
  4563. * @param {Integer} char1 The second byte
  4564. * @return {Boolean} Whether the 2 bytes are a PAC
  4565. */
  4566. Cea608Stream.prototype.isPAC = function(char0, char1) {
  4567. return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
  4568. (char1 >= 0x40 && char1 <= 0x7f));
  4569. };
  4570. /**
  4571. * Detects if a packet's second byte is in the range of a PAC color code
  4572. *
  4573. * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
  4574. * 0x60 to 0x6f.
  4575. *
  4576. * @param {Integer} char1 The second byte
  4577. * @return {Boolean} Whether the byte is a color PAC
  4578. */
  4579. Cea608Stream.prototype.isColorPAC = function(char1) {
  4580. return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
  4581. };
  4582. /**
  4583. * Detects if a single byte is in the range of a normal character
  4584. *
  4585. * Normal text bytes are in the range 0x20 to 0x7f.
  4586. *
  4587. * @param {Integer} char The byte
  4588. * @return {Boolean} Whether the byte is a normal character
  4589. */
  4590. Cea608Stream.prototype.isNormalChar = function(char) {
  4591. return (char >= 0x20 && char <= 0x7f);
  4592. };
  4593. // Adds the opening HTML tag for the passed character to the caption text,
  4594. // and keeps track of it for later closing
  4595. Cea608Stream.prototype.addFormatting = function(pts, format) {
  4596. this.formatting_ = this.formatting_.concat(format);
  4597. var text = format.reduce(function(text, format) {
  4598. return text + '<' + format + '>';
  4599. }, '');
  4600. this[this.mode_](pts, text);
  4601. };
  4602. // Adds HTML closing tags for current formatting to caption text and
  4603. // clears remembered formatting
  4604. Cea608Stream.prototype.clearFormatting = function(pts) {
  4605. if (!this.formatting_.length) {
  4606. return;
  4607. }
  4608. var text = this.formatting_.reverse().reduce(function(text, format) {
  4609. return text + '</' + format + '>';
  4610. }, '');
  4611. this.formatting_ = [];
  4612. this[this.mode_](pts, text);
  4613. };
  4614. // Mode Implementations
  4615. Cea608Stream.prototype.popOn = function(pts, text) {
  4616. var baseRow = this.nonDisplayed_[this.row_];
  4617. // buffer characters
  4618. baseRow += text;
  4619. this.nonDisplayed_[this.row_] = baseRow;
  4620. };
  4621. Cea608Stream.prototype.rollUp = function(pts, text) {
  4622. var baseRow = this.displayed_[BOTTOM_ROW];
  4623. baseRow += text;
  4624. this.displayed_[BOTTOM_ROW] = baseRow;
  4625. };
  4626. Cea608Stream.prototype.shiftRowsUp_ = function() {
  4627. var i;
  4628. // clear out inactive rows
  4629. for (i = 0; i < this.topRow_; i++) {
  4630. this.displayed_[i] = '';
  4631. }
  4632. // shift displayed rows up
  4633. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  4634. this.displayed_[i] = this.displayed_[i + 1];
  4635. }
  4636. // clear out the bottom row
  4637. this.displayed_[BOTTOM_ROW] = '';
  4638. };
  4639. // paintOn mode is not implemented
  4640. Cea608Stream.prototype.paintOn = function() {};
  4641. // exports
  4642. module.exports = {
  4643. CaptionStream: CaptionStream,
  4644. Cea608Stream: Cea608Stream
  4645. };
  4646. },{"../utils/stream":35}],26:[function(require,module,exports){
  4647. /**
  4648. * mux.js
  4649. *
  4650. * Copyright (c) 2015 Brightcove
  4651. * All rights reserved.
  4652. *
  4653. * A stream-based mp2t to mp4 converter. This utility can be used to
  4654. * deliver mp4s to a SourceBuffer on platforms that support native
  4655. * Media Source Extensions.
  4656. */
  4657. 'use strict';
  4658. var Stream = require('../utils/stream.js'),
  4659. CaptionStream = require('./caption-stream'),
  4660. StreamTypes = require('./stream-types'),
  4661. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  4662. var m2tsStreamTypes = require('./stream-types.js');
  4663. // object types
  4664. var TransportPacketStream, TransportParseStream, ElementaryStream;
  4665. // constants
  4666. var
  4667. MP2T_PACKET_LENGTH = 188, // bytes
  4668. SYNC_BYTE = 0x47;
  4669. /**
  4670. * Splits an incoming stream of binary data into MPEG-2 Transport
  4671. * Stream packets.
  4672. */
  4673. TransportPacketStream = function() {
  4674. var
  4675. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  4676. bytesInBuffer = 0;
  4677. TransportPacketStream.prototype.init.call(this);
  4678. // Deliver new bytes to the stream.
  4679. this.push = function(bytes) {
  4680. var
  4681. startIndex = 0,
  4682. endIndex = MP2T_PACKET_LENGTH,
  4683. everything;
  4684. // If there are bytes remaining from the last segment, prepend them to the
  4685. // bytes that were pushed in
  4686. if (bytesInBuffer) {
  4687. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  4688. everything.set(buffer.subarray(0, bytesInBuffer));
  4689. everything.set(bytes, bytesInBuffer);
  4690. bytesInBuffer = 0;
  4691. } else {
  4692. everything = bytes;
  4693. }
  4694. // While we have enough data for a packet
  4695. while (endIndex < everything.byteLength) {
  4696. // Look for a pair of start and end sync bytes in the data..
  4697. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  4698. // We found a packet so emit it and jump one whole packet forward in
  4699. // the stream
  4700. this.trigger('data', everything.subarray(startIndex, endIndex));
  4701. startIndex += MP2T_PACKET_LENGTH;
  4702. endIndex += MP2T_PACKET_LENGTH;
  4703. continue;
  4704. }
  4705. // If we get here, we have somehow become de-synchronized and we need to step
  4706. // forward one byte at a time until we find a pair of sync bytes that denote
  4707. // a packet
  4708. startIndex++;
  4709. endIndex++;
  4710. }
  4711. // If there was some data left over at the end of the segment that couldn't
  4712. // possibly be a whole packet, keep it because it might be the start of a packet
  4713. // that continues in the next segment
  4714. if (startIndex < everything.byteLength) {
  4715. buffer.set(everything.subarray(startIndex), 0);
  4716. bytesInBuffer = everything.byteLength - startIndex;
  4717. }
  4718. };
  4719. this.flush = function() {
  4720. // If the buffer contains a whole packet when we are being flushed, emit it
  4721. // and empty the buffer. Otherwise hold onto the data because it may be
  4722. // important for decoding the next segment
  4723. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  4724. this.trigger('data', buffer);
  4725. bytesInBuffer = 0;
  4726. }
  4727. this.trigger('done');
  4728. };
  4729. };
  4730. TransportPacketStream.prototype = new Stream();
  4731. /**
  4732. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  4733. * forms of the individual transport stream packets.
  4734. */
  4735. TransportParseStream = function() {
  4736. var parsePsi, parsePat, parsePmt, self;
  4737. TransportParseStream.prototype.init.call(this);
  4738. self = this;
  4739. this.packetsWaitingForPmt = [];
  4740. this.programMapTable = undefined;
  4741. parsePsi = function(payload, psi) {
  4742. var offset = 0;
  4743. // PSI packets may be split into multiple sections and those
  4744. // sections may be split into multiple packets. If a PSI
  4745. // section starts in this packet, the payload_unit_start_indicator
  4746. // will be true and the first byte of the payload will indicate
  4747. // the offset from the current position to the start of the
  4748. // section.
  4749. if (psi.payloadUnitStartIndicator) {
  4750. offset += payload[offset] + 1;
  4751. }
  4752. if (psi.type === 'pat') {
  4753. parsePat(payload.subarray(offset), psi);
  4754. } else {
  4755. parsePmt(payload.subarray(offset), psi);
  4756. }
  4757. };
  4758. parsePat = function(payload, pat) {
  4759. pat.section_number = payload[7]; // eslint-disable-line camelcase
  4760. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  4761. // skip the PSI header and parse the first PMT entry
  4762. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  4763. pat.pmtPid = self.pmtPid;
  4764. };
  4765. /**
  4766. * Parse out the relevant fields of a Program Map Table (PMT).
  4767. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  4768. * packet. The first byte in this array should be the table_id
  4769. * field.
  4770. * @param pmt {object} the object that should be decorated with
  4771. * fields parsed from the PMT.
  4772. */
  4773. parsePmt = function(payload, pmt) {
  4774. var sectionLength, tableEnd, programInfoLength, offset;
  4775. // PMTs can be sent ahead of the time when they should actually
  4776. // take effect. We don't believe this should ever be the case
  4777. // for HLS but we'll ignore "forward" PMT declarations if we see
  4778. // them. Future PMT declarations have the current_next_indicator
  4779. // set to zero.
  4780. if (!(payload[5] & 0x01)) {
  4781. return;
  4782. }
  4783. // overwrite any existing program map table
  4784. self.programMapTable = {
  4785. video: null,
  4786. audio: null,
  4787. 'timed-metadata': {}
  4788. };
  4789. // the mapping table ends at the end of the current section
  4790. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  4791. tableEnd = 3 + sectionLength - 4;
  4792. // to determine where the table is, we have to figure out how
  4793. // long the program info descriptors are
  4794. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  4795. // advance the offset to the first entry in the mapping table
  4796. offset = 12 + programInfoLength;
  4797. while (offset < tableEnd) {
  4798. var streamType = payload[offset];
  4799. var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
  4800. // only map a single elementary_pid for audio and video stream types
  4801. // TODO: should this be done for metadata too? for now maintain behavior of
  4802. // multiple metadata streams
  4803. if (streamType === StreamTypes.H264_STREAM_TYPE &&
  4804. self.programMapTable.video === null) {
  4805. self.programMapTable.video = pid;
  4806. } else if (streamType === StreamTypes.ADTS_STREAM_TYPE &&
  4807. self.programMapTable.audio === null) {
  4808. self.programMapTable.audio = pid;
  4809. } else if (streamType === StreamTypes.METADATA_STREAM_TYPE) {
  4810. // map pid to stream type for metadata streams
  4811. self.programMapTable['timed-metadata'][pid] = streamType;
  4812. }
  4813. // move to the next table entry
  4814. // skip past the elementary stream descriptors, if present
  4815. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  4816. }
  4817. // record the map on the packet as well
  4818. pmt.programMapTable = self.programMapTable;
  4819. };
  4820. /**
  4821. * Deliver a new MP2T packet to the stream.
  4822. */
  4823. this.push = function(packet) {
  4824. var
  4825. result = {},
  4826. offset = 4;
  4827. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  4828. // pid is a 13-bit field starting at the last bit of packet[1]
  4829. result.pid = packet[1] & 0x1f;
  4830. result.pid <<= 8;
  4831. result.pid |= packet[2];
  4832. // if an adaption field is present, its length is specified by the
  4833. // fifth byte of the TS packet header. The adaptation field is
  4834. // used to add stuffing to PES packets that don't fill a complete
  4835. // TS packet, and to specify some forms of timing and control data
  4836. // that we do not currently use.
  4837. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  4838. offset += packet[offset] + 1;
  4839. }
  4840. // parse the rest of the packet based on the type
  4841. if (result.pid === 0) {
  4842. result.type = 'pat';
  4843. parsePsi(packet.subarray(offset), result);
  4844. this.trigger('data', result);
  4845. } else if (result.pid === this.pmtPid) {
  4846. result.type = 'pmt';
  4847. parsePsi(packet.subarray(offset), result);
  4848. this.trigger('data', result);
  4849. // if there are any packets waiting for a PMT to be found, process them now
  4850. while (this.packetsWaitingForPmt.length) {
  4851. this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
  4852. }
  4853. } else if (this.programMapTable === undefined) {
  4854. // When we have not seen a PMT yet, defer further processing of
  4855. // PES packets until one has been parsed
  4856. this.packetsWaitingForPmt.push([packet, offset, result]);
  4857. } else {
  4858. this.processPes_(packet, offset, result);
  4859. }
  4860. };
  4861. this.processPes_ = function(packet, offset, result) {
  4862. // set the appropriate stream type
  4863. if (result.pid === this.programMapTable.video) {
  4864. result.streamType = StreamTypes.H264_STREAM_TYPE;
  4865. } else if (result.pid === this.programMapTable.audio) {
  4866. result.streamType = StreamTypes.ADTS_STREAM_TYPE;
  4867. } else {
  4868. // if not video or audio, it is timed-metadata or unknown
  4869. // if unknown, streamType will be undefined
  4870. result.streamType = this.programMapTable['timed-metadata'][result.pid];
  4871. }
  4872. result.type = 'pes';
  4873. result.data = packet.subarray(offset);
  4874. this.trigger('data', result);
  4875. };
  4876. };
  4877. TransportParseStream.prototype = new Stream();
  4878. TransportParseStream.STREAM_TYPES = {
  4879. h264: 0x1b,
  4880. adts: 0x0f
  4881. };
  4882. /**
  4883. * Reconsistutes program elementary stream (PES) packets from parsed
  4884. * transport stream packets. That is, if you pipe an
  4885. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  4886. * events will be events which capture the bytes for individual PES
  4887. * packets plus relevant metadata that has been extracted from the
  4888. * container.
  4889. */
  4890. ElementaryStream = function() {
  4891. var
  4892. self = this,
  4893. // PES packet fragments
  4894. video = {
  4895. data: [],
  4896. size: 0
  4897. },
  4898. audio = {
  4899. data: [],
  4900. size: 0
  4901. },
  4902. timedMetadata = {
  4903. data: [],
  4904. size: 0
  4905. },
  4906. parsePes = function(payload, pes) {
  4907. var ptsDtsFlags;
  4908. // get the packet length, this will be 0 for video
  4909. pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
  4910. // find out if this packets starts a new keyframe
  4911. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  4912. // PES packets may be annotated with a PTS value, or a PTS value
  4913. // and a DTS value. Determine what combination of values is
  4914. // available to work with.
  4915. ptsDtsFlags = payload[7];
  4916. // PTS and DTS are normally stored as a 33-bit number. Javascript
  4917. // performs all bitwise operations on 32-bit integers but javascript
  4918. // supports a much greater range (52-bits) of integer using standard
  4919. // mathematical operations.
  4920. // We construct a 31-bit value using bitwise operators over the 31
  4921. // most significant bits and then multiply by 4 (equal to a left-shift
  4922. // of 2) before we add the final 2 least significant bits of the
  4923. // timestamp (equal to an OR.)
  4924. if (ptsDtsFlags & 0xC0) {
  4925. // the PTS and DTS are not written out directly. For information
  4926. // on how they are encoded, see
  4927. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  4928. pes.pts = (payload[9] & 0x0E) << 27 |
  4929. (payload[10] & 0xFF) << 20 |
  4930. (payload[11] & 0xFE) << 12 |
  4931. (payload[12] & 0xFF) << 5 |
  4932. (payload[13] & 0xFE) >>> 3;
  4933. pes.pts *= 4; // Left shift by 2
  4934. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  4935. pes.dts = pes.pts;
  4936. if (ptsDtsFlags & 0x40) {
  4937. pes.dts = (payload[14] & 0x0E) << 27 |
  4938. (payload[15] & 0xFF) << 20 |
  4939. (payload[16] & 0xFE) << 12 |
  4940. (payload[17] & 0xFF) << 5 |
  4941. (payload[18] & 0xFE) >>> 3;
  4942. pes.dts *= 4; // Left shift by 2
  4943. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  4944. }
  4945. }
  4946. // the data section starts immediately after the PES header.
  4947. // pes_header_data_length specifies the number of header bytes
  4948. // that follow the last byte of the field.
  4949. pes.data = payload.subarray(9 + payload[8]);
  4950. },
  4951. flushStream = function(stream, type, forceFlush) {
  4952. var
  4953. packetData = new Uint8Array(stream.size),
  4954. event = {
  4955. type: type
  4956. },
  4957. i = 0,
  4958. offset = 0,
  4959. packetFlushable = false,
  4960. fragment;
  4961. // do nothing if there is not enough buffered data for a complete
  4962. // PES header
  4963. if (!stream.data.length || stream.size < 9) {
  4964. return;
  4965. }
  4966. event.trackId = stream.data[0].pid;
  4967. // reassemble the packet
  4968. for (i = 0; i < stream.data.length; i++) {
  4969. fragment = stream.data[i];
  4970. packetData.set(fragment.data, offset);
  4971. offset += fragment.data.byteLength;
  4972. }
  4973. // parse assembled packet's PES header
  4974. parsePes(packetData, event);
  4975. // non-video PES packets MUST have a non-zero PES_packet_length
  4976. // check that there is enough stream data to fill the packet
  4977. packetFlushable = type === 'video' || event.packetLength <= stream.size;
  4978. // flush pending packets if the conditions are right
  4979. if (forceFlush || packetFlushable) {
  4980. stream.size = 0;
  4981. stream.data.length = 0;
  4982. }
  4983. // only emit packets that are complete. this is to avoid assembling
  4984. // incomplete PES packets due to poor segmentation
  4985. if (packetFlushable) {
  4986. self.trigger('data', event);
  4987. }
  4988. };
  4989. ElementaryStream.prototype.init.call(this);
  4990. this.push = function(data) {
  4991. ({
  4992. pat: function() {
  4993. // we have to wait for the PMT to arrive as well before we
  4994. // have any meaningful metadata
  4995. },
  4996. pes: function() {
  4997. var stream, streamType;
  4998. switch (data.streamType) {
  4999. case StreamTypes.H264_STREAM_TYPE:
  5000. case m2tsStreamTypes.H264_STREAM_TYPE:
  5001. stream = video;
  5002. streamType = 'video';
  5003. break;
  5004. case StreamTypes.ADTS_STREAM_TYPE:
  5005. stream = audio;
  5006. streamType = 'audio';
  5007. break;
  5008. case StreamTypes.METADATA_STREAM_TYPE:
  5009. stream = timedMetadata;
  5010. streamType = 'timed-metadata';
  5011. break;
  5012. default:
  5013. // ignore unknown stream types
  5014. return;
  5015. }
  5016. // if a new packet is starting, we can flush the completed
  5017. // packet
  5018. if (data.payloadUnitStartIndicator) {
  5019. flushStream(stream, streamType, true);
  5020. }
  5021. // buffer this fragment until we are sure we've received the
  5022. // complete payload
  5023. stream.data.push(data);
  5024. stream.size += data.data.byteLength;
  5025. },
  5026. pmt: function() {
  5027. var
  5028. event = {
  5029. type: 'metadata',
  5030. tracks: []
  5031. },
  5032. programMapTable = data.programMapTable;
  5033. // translate audio and video streams to tracks
  5034. if (programMapTable.video !== null) {
  5035. event.tracks.push({
  5036. timelineStartInfo: {
  5037. baseMediaDecodeTime: 0
  5038. },
  5039. id: +programMapTable.video,
  5040. codec: 'avc',
  5041. type: 'video'
  5042. });
  5043. }
  5044. if (programMapTable.audio !== null) {
  5045. event.tracks.push({
  5046. timelineStartInfo: {
  5047. baseMediaDecodeTime: 0
  5048. },
  5049. id: +programMapTable.audio,
  5050. codec: 'adts',
  5051. type: 'audio'
  5052. });
  5053. }
  5054. self.trigger('data', event);
  5055. }
  5056. })[data.type]();
  5057. };
  5058. /**
  5059. * Flush any remaining input. Video PES packets may be of variable
  5060. * length. Normally, the start of a new video packet can trigger the
  5061. * finalization of the previous packet. That is not possible if no
  5062. * more video is forthcoming, however. In that case, some other
  5063. * mechanism (like the end of the file) has to be employed. When it is
  5064. * clear that no additional data is forthcoming, calling this method
  5065. * will flush the buffered packets.
  5066. */
  5067. this.flush = function() {
  5068. // !!THIS ORDER IS IMPORTANT!!
  5069. // video first then audio
  5070. flushStream(video, 'video');
  5071. flushStream(audio, 'audio');
  5072. flushStream(timedMetadata, 'timed-metadata');
  5073. this.trigger('done');
  5074. };
  5075. };
  5076. ElementaryStream.prototype = new Stream();
  5077. var m2ts = {
  5078. PAT_PID: 0x0000,
  5079. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  5080. TransportPacketStream: TransportPacketStream,
  5081. TransportParseStream: TransportParseStream,
  5082. ElementaryStream: ElementaryStream,
  5083. TimestampRolloverStream: TimestampRolloverStream,
  5084. CaptionStream: CaptionStream.CaptionStream,
  5085. Cea608Stream: CaptionStream.Cea608Stream,
  5086. MetadataStream: require('./metadata-stream')
  5087. };
  5088. for (var type in StreamTypes) {
  5089. if (StreamTypes.hasOwnProperty(type)) {
  5090. m2ts[type] = StreamTypes[type];
  5091. }
  5092. }
  5093. module.exports = m2ts;
  5094. },{"../utils/stream.js":35,"./caption-stream":25,"./metadata-stream":27,"./stream-types":28,"./stream-types.js":28,"./timestamp-rollover-stream":29}],27:[function(require,module,exports){
  5095. /**
  5096. * Accepts program elementary stream (PES) data events and parses out
  5097. * ID3 metadata from them, if present.
  5098. * @see http://id3.org/id3v2.3.0
  5099. */
  5100. 'use strict';
  5101. var
  5102. Stream = require('../utils/stream'),
  5103. StreamTypes = require('./stream-types'),
  5104. // return a percent-encoded representation of the specified byte range
  5105. // @see http://en.wikipedia.org/wiki/Percent-encoding
  5106. percentEncode = function(bytes, start, end) {
  5107. var i, result = '';
  5108. for (i = start; i < end; i++) {
  5109. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  5110. }
  5111. return result;
  5112. },
  5113. // return the string representation of the specified byte range,
  5114. // interpreted as UTf-8.
  5115. parseUtf8 = function(bytes, start, end) {
  5116. return decodeURIComponent(percentEncode(bytes, start, end));
  5117. },
  5118. // return the string representation of the specified byte range,
  5119. // interpreted as ISO-8859-1.
  5120. parseIso88591 = function(bytes, start, end) {
  5121. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  5122. },
  5123. parseSyncSafeInteger = function(data) {
  5124. return (data[0] << 21) |
  5125. (data[1] << 14) |
  5126. (data[2] << 7) |
  5127. (data[3]);
  5128. },
  5129. tagParsers = {
  5130. TXXX: function(tag) {
  5131. var i;
  5132. if (tag.data[0] !== 3) {
  5133. // ignore frames with unrecognized character encodings
  5134. return;
  5135. }
  5136. for (i = 1; i < tag.data.length; i++) {
  5137. if (tag.data[i] === 0) {
  5138. // parse the text fields
  5139. tag.description = parseUtf8(tag.data, 1, i);
  5140. // do not include the null terminator in the tag value
  5141. tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
  5142. break;
  5143. }
  5144. }
  5145. tag.data = tag.value;
  5146. },
  5147. WXXX: function(tag) {
  5148. var i;
  5149. if (tag.data[0] !== 3) {
  5150. // ignore frames with unrecognized character encodings
  5151. return;
  5152. }
  5153. for (i = 1; i < tag.data.length; i++) {
  5154. if (tag.data[i] === 0) {
  5155. // parse the description and URL fields
  5156. tag.description = parseUtf8(tag.data, 1, i);
  5157. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  5158. break;
  5159. }
  5160. }
  5161. },
  5162. PRIV: function(tag) {
  5163. var i;
  5164. for (i = 0; i < tag.data.length; i++) {
  5165. if (tag.data[i] === 0) {
  5166. // parse the description and URL fields
  5167. tag.owner = parseIso88591(tag.data, 0, i);
  5168. break;
  5169. }
  5170. }
  5171. tag.privateData = tag.data.subarray(i + 1);
  5172. tag.data = tag.privateData;
  5173. }
  5174. },
  5175. MetadataStream;
  5176. MetadataStream = function(options) {
  5177. var
  5178. settings = {
  5179. debug: !!(options && options.debug),
  5180. // the bytes of the program-level descriptor field in MP2T
  5181. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  5182. // program element descriptors"
  5183. descriptor: options && options.descriptor
  5184. },
  5185. // the total size in bytes of the ID3 tag being parsed
  5186. tagSize = 0,
  5187. // tag data that is not complete enough to be parsed
  5188. buffer = [],
  5189. // the total number of bytes currently in the buffer
  5190. bufferSize = 0,
  5191. i;
  5192. MetadataStream.prototype.init.call(this);
  5193. // calculate the text track in-band metadata track dispatch type
  5194. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  5195. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  5196. if (settings.descriptor) {
  5197. for (i = 0; i < settings.descriptor.length; i++) {
  5198. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  5199. }
  5200. }
  5201. this.push = function(chunk) {
  5202. var tag, frameStart, frameSize, frame, i, frameHeader;
  5203. if (chunk.type !== 'timed-metadata') {
  5204. return;
  5205. }
  5206. // if data_alignment_indicator is set in the PES header,
  5207. // we must have the start of a new ID3 tag. Assume anything
  5208. // remaining in the buffer was malformed and throw it out
  5209. if (chunk.dataAlignmentIndicator) {
  5210. bufferSize = 0;
  5211. buffer.length = 0;
  5212. }
  5213. // ignore events that don't look like ID3 data
  5214. if (buffer.length === 0 &&
  5215. (chunk.data.length < 10 ||
  5216. chunk.data[0] !== 'I'.charCodeAt(0) ||
  5217. chunk.data[1] !== 'D'.charCodeAt(0) ||
  5218. chunk.data[2] !== '3'.charCodeAt(0))) {
  5219. if (settings.debug) {
  5220. // eslint-disable-next-line no-console
  5221. console.log('Skipping unrecognized metadata packet');
  5222. }
  5223. return;
  5224. }
  5225. // add this chunk to the data we've collected so far
  5226. buffer.push(chunk);
  5227. bufferSize += chunk.data.byteLength;
  5228. // grab the size of the entire frame from the ID3 header
  5229. if (buffer.length === 1) {
  5230. // the frame size is transmitted as a 28-bit integer in the
  5231. // last four bytes of the ID3 header.
  5232. // The most significant bit of each byte is dropped and the
  5233. // results concatenated to recover the actual value.
  5234. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  5235. // ID3 reports the tag size excluding the header but it's more
  5236. // convenient for our comparisons to include it
  5237. tagSize += 10;
  5238. }
  5239. // if the entire frame has not arrived, wait for more data
  5240. if (bufferSize < tagSize) {
  5241. return;
  5242. }
  5243. // collect the entire frame so it can be parsed
  5244. tag = {
  5245. data: new Uint8Array(tagSize),
  5246. frames: [],
  5247. pts: buffer[0].pts,
  5248. dts: buffer[0].dts
  5249. };
  5250. for (i = 0; i < tagSize;) {
  5251. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  5252. i += buffer[0].data.byteLength;
  5253. bufferSize -= buffer[0].data.byteLength;
  5254. buffer.shift();
  5255. }
  5256. // find the start of the first frame and the end of the tag
  5257. frameStart = 10;
  5258. if (tag.data[5] & 0x40) {
  5259. // advance the frame start past the extended header
  5260. frameStart += 4; // header size field
  5261. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  5262. // clip any padding off the end
  5263. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  5264. }
  5265. // parse one or more ID3 frames
  5266. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  5267. do {
  5268. // determine the number of bytes in this frame
  5269. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  5270. if (frameSize < 1) {
  5271. // eslint-disable-next-line no-console
  5272. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  5273. }
  5274. frameHeader = String.fromCharCode(tag.data[frameStart],
  5275. tag.data[frameStart + 1],
  5276. tag.data[frameStart + 2],
  5277. tag.data[frameStart + 3]);
  5278. frame = {
  5279. id: frameHeader,
  5280. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  5281. };
  5282. frame.key = frame.id;
  5283. if (tagParsers[frame.id]) {
  5284. tagParsers[frame.id](frame);
  5285. // handle the special PRIV frame used to indicate the start
  5286. // time for raw AAC data
  5287. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  5288. var
  5289. d = frame.data,
  5290. size = ((d[3] & 0x01) << 30) |
  5291. (d[4] << 22) |
  5292. (d[5] << 14) |
  5293. (d[6] << 6) |
  5294. (d[7] >>> 2);
  5295. size *= 4;
  5296. size += d[7] & 0x03;
  5297. frame.timeStamp = size;
  5298. // in raw AAC, all subsequent data will be timestamped based
  5299. // on the value of this frame
  5300. // we couldn't have known the appropriate pts and dts before
  5301. // parsing this ID3 tag so set those values now
  5302. if (tag.pts === undefined && tag.dts === undefined) {
  5303. tag.pts = frame.timeStamp;
  5304. tag.dts = frame.timeStamp;
  5305. }
  5306. this.trigger('timestamp', frame);
  5307. }
  5308. }
  5309. tag.frames.push(frame);
  5310. frameStart += 10; // advance past the frame header
  5311. frameStart += frameSize; // advance past the frame body
  5312. } while (frameStart < tagSize);
  5313. this.trigger('data', tag);
  5314. };
  5315. };
  5316. MetadataStream.prototype = new Stream();
  5317. module.exports = MetadataStream;
  5318. },{"../utils/stream":35,"./stream-types":28}],28:[function(require,module,exports){
  5319. 'use strict';
  5320. module.exports = {
  5321. H264_STREAM_TYPE: 0x1B,
  5322. ADTS_STREAM_TYPE: 0x0F,
  5323. METADATA_STREAM_TYPE: 0x15
  5324. };
  5325. },{}],29:[function(require,module,exports){
  5326. /**
  5327. * mux.js
  5328. *
  5329. * Copyright (c) 2016 Brightcove
  5330. * All rights reserved.
  5331. *
  5332. * Accepts program elementary stream (PES) data events and corrects
  5333. * decode and presentation time stamps to account for a rollover
  5334. * of the 33 bit value.
  5335. */
  5336. 'use strict';
  5337. var Stream = require('../utils/stream');
  5338. var MAX_TS = 8589934592;
  5339. var RO_THRESH = 4294967296;
  5340. var handleRollover = function(value, reference) {
  5341. var direction = 1;
  5342. if (value > reference) {
  5343. // If the current timestamp value is greater than our reference timestamp and we detect a
  5344. // timestamp rollover, this means the roll over is happening in the opposite direction.
  5345. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  5346. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  5347. // rollover point. In loading this segment, the timestamp values will be very large,
  5348. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  5349. // the time stamp to be `value - 2^33`.
  5350. direction = -1;
  5351. }
  5352. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  5353. // cause an incorrect adjustment.
  5354. while (Math.abs(reference - value) > RO_THRESH) {
  5355. value += (direction * MAX_TS);
  5356. }
  5357. return value;
  5358. };
  5359. var TimestampRolloverStream = function(type) {
  5360. var lastDTS, referenceDTS;
  5361. TimestampRolloverStream.prototype.init.call(this);
  5362. this.type_ = type;
  5363. this.push = function(data) {
  5364. if (data.type !== this.type_) {
  5365. return;
  5366. }
  5367. if (referenceDTS === undefined) {
  5368. referenceDTS = data.dts;
  5369. }
  5370. data.dts = handleRollover(data.dts, referenceDTS);
  5371. data.pts = handleRollover(data.pts, referenceDTS);
  5372. lastDTS = data.dts;
  5373. this.trigger('data', data);
  5374. };
  5375. this.flush = function() {
  5376. referenceDTS = lastDTS;
  5377. this.trigger('done');
  5378. };
  5379. this.discontinuity = function() {
  5380. referenceDTS = void 0;
  5381. lastDTS = void 0;
  5382. };
  5383. };
  5384. TimestampRolloverStream.prototype = new Stream();
  5385. module.exports = {
  5386. TimestampRolloverStream: TimestampRolloverStream,
  5387. handleRollover: handleRollover
  5388. };
  5389. },{"../utils/stream":35}],30:[function(require,module,exports){
  5390. module.exports = {
  5391. generator: require('./mp4-generator'),
  5392. Transmuxer: require('./transmuxer').Transmuxer,
  5393. AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
  5394. VideoSegmentStream: require('./transmuxer').VideoSegmentStream
  5395. };
  5396. },{"./mp4-generator":31,"./transmuxer":32}],31:[function(require,module,exports){
  5397. /**
  5398. * mux.js
  5399. *
  5400. * Copyright (c) 2015 Brightcove
  5401. * All rights reserved.
  5402. *
  5403. * Functions that generate fragmented MP4s suitable for use with Media
  5404. * Source Extensions.
  5405. */
  5406. 'use strict';
  5407. var UINT32_MAX = Math.pow(2, 32) - 1;
  5408. var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
  5409. trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
  5410. trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
  5411. AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
  5412. // pre-calculate constants
  5413. (function() {
  5414. var i;
  5415. types = {
  5416. avc1: [], // codingname
  5417. avcC: [],
  5418. btrt: [],
  5419. dinf: [],
  5420. dref: [],
  5421. esds: [],
  5422. ftyp: [],
  5423. hdlr: [],
  5424. mdat: [],
  5425. mdhd: [],
  5426. mdia: [],
  5427. mfhd: [],
  5428. minf: [],
  5429. moof: [],
  5430. moov: [],
  5431. mp4a: [], // codingname
  5432. mvex: [],
  5433. mvhd: [],
  5434. sdtp: [],
  5435. smhd: [],
  5436. stbl: [],
  5437. stco: [],
  5438. stsc: [],
  5439. stsd: [],
  5440. stsz: [],
  5441. stts: [],
  5442. styp: [],
  5443. tfdt: [],
  5444. tfhd: [],
  5445. traf: [],
  5446. trak: [],
  5447. trun: [],
  5448. trex: [],
  5449. tkhd: [],
  5450. vmhd: []
  5451. };
  5452. // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  5453. // don't throw an error
  5454. if (typeof Uint8Array === 'undefined') {
  5455. return;
  5456. }
  5457. for (i in types) {
  5458. if (types.hasOwnProperty(i)) {
  5459. types[i] = [
  5460. i.charCodeAt(0),
  5461. i.charCodeAt(1),
  5462. i.charCodeAt(2),
  5463. i.charCodeAt(3)
  5464. ];
  5465. }
  5466. }
  5467. MAJOR_BRAND = new Uint8Array([
  5468. 'i'.charCodeAt(0),
  5469. 's'.charCodeAt(0),
  5470. 'o'.charCodeAt(0),
  5471. 'm'.charCodeAt(0)
  5472. ]);
  5473. AVC1_BRAND = new Uint8Array([
  5474. 'a'.charCodeAt(0),
  5475. 'v'.charCodeAt(0),
  5476. 'c'.charCodeAt(0),
  5477. '1'.charCodeAt(0)
  5478. ]);
  5479. MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  5480. VIDEO_HDLR = new Uint8Array([
  5481. 0x00, // version 0
  5482. 0x00, 0x00, 0x00, // flags
  5483. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5484. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  5485. 0x00, 0x00, 0x00, 0x00, // reserved
  5486. 0x00, 0x00, 0x00, 0x00, // reserved
  5487. 0x00, 0x00, 0x00, 0x00, // reserved
  5488. 0x56, 0x69, 0x64, 0x65,
  5489. 0x6f, 0x48, 0x61, 0x6e,
  5490. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  5491. ]);
  5492. AUDIO_HDLR = new Uint8Array([
  5493. 0x00, // version 0
  5494. 0x00, 0x00, 0x00, // flags
  5495. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5496. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  5497. 0x00, 0x00, 0x00, 0x00, // reserved
  5498. 0x00, 0x00, 0x00, 0x00, // reserved
  5499. 0x00, 0x00, 0x00, 0x00, // reserved
  5500. 0x53, 0x6f, 0x75, 0x6e,
  5501. 0x64, 0x48, 0x61, 0x6e,
  5502. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  5503. ]);
  5504. HDLR_TYPES = {
  5505. video: VIDEO_HDLR,
  5506. audio: AUDIO_HDLR
  5507. };
  5508. DREF = new Uint8Array([
  5509. 0x00, // version 0
  5510. 0x00, 0x00, 0x00, // flags
  5511. 0x00, 0x00, 0x00, 0x01, // entry_count
  5512. 0x00, 0x00, 0x00, 0x0c, // entry_size
  5513. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  5514. 0x00, // version 0
  5515. 0x00, 0x00, 0x01 // entry_flags
  5516. ]);
  5517. SMHD = new Uint8Array([
  5518. 0x00, // version
  5519. 0x00, 0x00, 0x00, // flags
  5520. 0x00, 0x00, // balance, 0 means centered
  5521. 0x00, 0x00 // reserved
  5522. ]);
  5523. STCO = new Uint8Array([
  5524. 0x00, // version
  5525. 0x00, 0x00, 0x00, // flags
  5526. 0x00, 0x00, 0x00, 0x00 // entry_count
  5527. ]);
  5528. STSC = STCO;
  5529. STSZ = new Uint8Array([
  5530. 0x00, // version
  5531. 0x00, 0x00, 0x00, // flags
  5532. 0x00, 0x00, 0x00, 0x00, // sample_size
  5533. 0x00, 0x00, 0x00, 0x00 // sample_count
  5534. ]);
  5535. STTS = STCO;
  5536. VMHD = new Uint8Array([
  5537. 0x00, // version
  5538. 0x00, 0x00, 0x01, // flags
  5539. 0x00, 0x00, // graphicsmode
  5540. 0x00, 0x00,
  5541. 0x00, 0x00,
  5542. 0x00, 0x00 // opcolor
  5543. ]);
  5544. }());
  5545. box = function(type) {
  5546. var
  5547. payload = [],
  5548. size = 0,
  5549. i,
  5550. result,
  5551. view;
  5552. for (i = 1; i < arguments.length; i++) {
  5553. payload.push(arguments[i]);
  5554. }
  5555. i = payload.length;
  5556. // calculate the total size we need to allocate
  5557. while (i--) {
  5558. size += payload[i].byteLength;
  5559. }
  5560. result = new Uint8Array(size + 8);
  5561. view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  5562. view.setUint32(0, result.byteLength);
  5563. result.set(type, 4);
  5564. // copy the payload into the result
  5565. for (i = 0, size = 8; i < payload.length; i++) {
  5566. result.set(payload[i], size);
  5567. size += payload[i].byteLength;
  5568. }
  5569. return result;
  5570. };
  5571. dinf = function() {
  5572. return box(types.dinf, box(types.dref, DREF));
  5573. };
  5574. esds = function(track) {
  5575. return box(types.esds, new Uint8Array([
  5576. 0x00, // version
  5577. 0x00, 0x00, 0x00, // flags
  5578. // ES_Descriptor
  5579. 0x03, // tag, ES_DescrTag
  5580. 0x19, // length
  5581. 0x00, 0x00, // ES_ID
  5582. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  5583. // DecoderConfigDescriptor
  5584. 0x04, // tag, DecoderConfigDescrTag
  5585. 0x11, // length
  5586. 0x40, // object type
  5587. 0x15, // streamType
  5588. 0x00, 0x06, 0x00, // bufferSizeDB
  5589. 0x00, 0x00, 0xda, 0xc0, // maxBitrate
  5590. 0x00, 0x00, 0xda, 0xc0, // avgBitrate
  5591. // DecoderSpecificInfo
  5592. 0x05, // tag, DecoderSpecificInfoTag
  5593. 0x02, // length
  5594. // ISO/IEC 14496-3, AudioSpecificConfig
  5595. // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  5596. (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
  5597. (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
  5598. 0x06, 0x01, 0x02 // GASpecificConfig
  5599. ]));
  5600. };
  5601. ftyp = function() {
  5602. return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
  5603. };
  5604. hdlr = function(type) {
  5605. return box(types.hdlr, HDLR_TYPES[type]);
  5606. };
  5607. mdat = function(data) {
  5608. return box(types.mdat, data);
  5609. };
  5610. mdhd = function(track) {
  5611. var result = new Uint8Array([
  5612. 0x00, // version 0
  5613. 0x00, 0x00, 0x00, // flags
  5614. 0x00, 0x00, 0x00, 0x02, // creation_time
  5615. 0x00, 0x00, 0x00, 0x03, // modification_time
  5616. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  5617. (track.duration >>> 24) & 0xFF,
  5618. (track.duration >>> 16) & 0xFF,
  5619. (track.duration >>> 8) & 0xFF,
  5620. track.duration & 0xFF, // duration
  5621. 0x55, 0xc4, // 'und' language (undetermined)
  5622. 0x00, 0x00
  5623. ]);
  5624. // Use the sample rate from the track metadata, when it is
  5625. // defined. The sample rate can be parsed out of an ADTS header, for
  5626. // instance.
  5627. if (track.samplerate) {
  5628. result[12] = (track.samplerate >>> 24) & 0xFF;
  5629. result[13] = (track.samplerate >>> 16) & 0xFF;
  5630. result[14] = (track.samplerate >>> 8) & 0xFF;
  5631. result[15] = (track.samplerate) & 0xFF;
  5632. }
  5633. return box(types.mdhd, result);
  5634. };
  5635. mdia = function(track) {
  5636. return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
  5637. };
  5638. mfhd = function(sequenceNumber) {
  5639. return box(types.mfhd, new Uint8Array([
  5640. 0x00,
  5641. 0x00, 0x00, 0x00, // flags
  5642. (sequenceNumber & 0xFF000000) >> 24,
  5643. (sequenceNumber & 0xFF0000) >> 16,
  5644. (sequenceNumber & 0xFF00) >> 8,
  5645. sequenceNumber & 0xFF // sequence_number
  5646. ]));
  5647. };
  5648. minf = function(track) {
  5649. return box(types.minf,
  5650. track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
  5651. dinf(),
  5652. stbl(track));
  5653. };
  5654. moof = function(sequenceNumber, tracks) {
  5655. var
  5656. trackFragments = [],
  5657. i = tracks.length;
  5658. // build traf boxes for each track fragment
  5659. while (i--) {
  5660. trackFragments[i] = traf(tracks[i]);
  5661. }
  5662. return box.apply(null, [
  5663. types.moof,
  5664. mfhd(sequenceNumber)
  5665. ].concat(trackFragments));
  5666. };
  5667. /**
  5668. * Returns a movie box.
  5669. * @param tracks {array} the tracks associated with this movie
  5670. * @see ISO/IEC 14496-12:2012(E), section 8.2.1
  5671. */
  5672. moov = function(tracks) {
  5673. var
  5674. i = tracks.length,
  5675. boxes = [];
  5676. while (i--) {
  5677. boxes[i] = trak(tracks[i]);
  5678. }
  5679. return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
  5680. };
  5681. mvex = function(tracks) {
  5682. var
  5683. i = tracks.length,
  5684. boxes = [];
  5685. while (i--) {
  5686. boxes[i] = trex(tracks[i]);
  5687. }
  5688. return box.apply(null, [types.mvex].concat(boxes));
  5689. };
  5690. mvhd = function(duration) {
  5691. var
  5692. bytes = new Uint8Array([
  5693. 0x00, // version 0
  5694. 0x00, 0x00, 0x00, // flags
  5695. 0x00, 0x00, 0x00, 0x01, // creation_time
  5696. 0x00, 0x00, 0x00, 0x02, // modification_time
  5697. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  5698. (duration & 0xFF000000) >> 24,
  5699. (duration & 0xFF0000) >> 16,
  5700. (duration & 0xFF00) >> 8,
  5701. duration & 0xFF, // duration
  5702. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  5703. 0x01, 0x00, // 1.0 volume
  5704. 0x00, 0x00, // reserved
  5705. 0x00, 0x00, 0x00, 0x00, // reserved
  5706. 0x00, 0x00, 0x00, 0x00, // reserved
  5707. 0x00, 0x01, 0x00, 0x00,
  5708. 0x00, 0x00, 0x00, 0x00,
  5709. 0x00, 0x00, 0x00, 0x00,
  5710. 0x00, 0x00, 0x00, 0x00,
  5711. 0x00, 0x01, 0x00, 0x00,
  5712. 0x00, 0x00, 0x00, 0x00,
  5713. 0x00, 0x00, 0x00, 0x00,
  5714. 0x00, 0x00, 0x00, 0x00,
  5715. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  5716. 0x00, 0x00, 0x00, 0x00,
  5717. 0x00, 0x00, 0x00, 0x00,
  5718. 0x00, 0x00, 0x00, 0x00,
  5719. 0x00, 0x00, 0x00, 0x00,
  5720. 0x00, 0x00, 0x00, 0x00,
  5721. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5722. 0xff, 0xff, 0xff, 0xff // next_track_ID
  5723. ]);
  5724. return box(types.mvhd, bytes);
  5725. };
  5726. sdtp = function(track) {
  5727. var
  5728. samples = track.samples || [],
  5729. bytes = new Uint8Array(4 + samples.length),
  5730. flags,
  5731. i;
  5732. // leave the full box header (4 bytes) all zero
  5733. // write the sample table
  5734. for (i = 0; i < samples.length; i++) {
  5735. flags = samples[i].flags;
  5736. bytes[i + 4] = (flags.dependsOn << 4) |
  5737. (flags.isDependedOn << 2) |
  5738. (flags.hasRedundancy);
  5739. }
  5740. return box(types.sdtp,
  5741. bytes);
  5742. };
  5743. stbl = function(track) {
  5744. return box(types.stbl,
  5745. stsd(track),
  5746. box(types.stts, STTS),
  5747. box(types.stsc, STSC),
  5748. box(types.stsz, STSZ),
  5749. box(types.stco, STCO));
  5750. };
  5751. (function() {
  5752. var videoSample, audioSample;
  5753. stsd = function(track) {
  5754. return box(types.stsd, new Uint8Array([
  5755. 0x00, // version 0
  5756. 0x00, 0x00, 0x00, // flags
  5757. 0x00, 0x00, 0x00, 0x01
  5758. ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  5759. };
  5760. videoSample = function(track) {
  5761. var
  5762. sps = track.sps || [],
  5763. pps = track.pps || [],
  5764. sequenceParameterSets = [],
  5765. pictureParameterSets = [],
  5766. i;
  5767. // assemble the SPSs
  5768. for (i = 0; i < sps.length; i++) {
  5769. sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
  5770. sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
  5771. sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
  5772. }
  5773. // assemble the PPSs
  5774. for (i = 0; i < pps.length; i++) {
  5775. pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
  5776. pictureParameterSets.push((pps[i].byteLength & 0xFF));
  5777. pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
  5778. }
  5779. return box(types.avc1, new Uint8Array([
  5780. 0x00, 0x00, 0x00,
  5781. 0x00, 0x00, 0x00, // reserved
  5782. 0x00, 0x01, // data_reference_index
  5783. 0x00, 0x00, // pre_defined
  5784. 0x00, 0x00, // reserved
  5785. 0x00, 0x00, 0x00, 0x00,
  5786. 0x00, 0x00, 0x00, 0x00,
  5787. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5788. (track.width & 0xff00) >> 8,
  5789. track.width & 0xff, // width
  5790. (track.height & 0xff00) >> 8,
  5791. track.height & 0xff, // height
  5792. 0x00, 0x48, 0x00, 0x00, // horizresolution
  5793. 0x00, 0x48, 0x00, 0x00, // vertresolution
  5794. 0x00, 0x00, 0x00, 0x00, // reserved
  5795. 0x00, 0x01, // frame_count
  5796. 0x13,
  5797. 0x76, 0x69, 0x64, 0x65,
  5798. 0x6f, 0x6a, 0x73, 0x2d,
  5799. 0x63, 0x6f, 0x6e, 0x74,
  5800. 0x72, 0x69, 0x62, 0x2d,
  5801. 0x68, 0x6c, 0x73, 0x00,
  5802. 0x00, 0x00, 0x00, 0x00,
  5803. 0x00, 0x00, 0x00, 0x00,
  5804. 0x00, 0x00, 0x00, // compressorname
  5805. 0x00, 0x18, // depth = 24
  5806. 0x11, 0x11 // pre_defined = -1
  5807. ]), box(types.avcC, new Uint8Array([
  5808. 0x01, // configurationVersion
  5809. track.profileIdc, // AVCProfileIndication
  5810. track.profileCompatibility, // profile_compatibility
  5811. track.levelIdc, // AVCLevelIndication
  5812. 0xff // lengthSizeMinusOne, hard-coded to 4 bytes
  5813. ].concat([
  5814. sps.length // numOfSequenceParameterSets
  5815. ]).concat(sequenceParameterSets).concat([
  5816. pps.length // numOfPictureParameterSets
  5817. ]).concat(pictureParameterSets))), // "PPS"
  5818. box(types.btrt, new Uint8Array([
  5819. 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  5820. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  5821. 0x00, 0x2d, 0xc6, 0xc0
  5822. ])) // avgBitrate
  5823. );
  5824. };
  5825. audioSample = function(track) {
  5826. return box(types.mp4a, new Uint8Array([
  5827. // SampleEntry, ISO/IEC 14496-12
  5828. 0x00, 0x00, 0x00,
  5829. 0x00, 0x00, 0x00, // reserved
  5830. 0x00, 0x01, // data_reference_index
  5831. // AudioSampleEntry, ISO/IEC 14496-12
  5832. 0x00, 0x00, 0x00, 0x00, // reserved
  5833. 0x00, 0x00, 0x00, 0x00, // reserved
  5834. (track.channelcount & 0xff00) >> 8,
  5835. (track.channelcount & 0xff), // channelcount
  5836. (track.samplesize & 0xff00) >> 8,
  5837. (track.samplesize & 0xff), // samplesize
  5838. 0x00, 0x00, // pre_defined
  5839. 0x00, 0x00, // reserved
  5840. (track.samplerate & 0xff00) >> 8,
  5841. (track.samplerate & 0xff),
  5842. 0x00, 0x00 // samplerate, 16.16
  5843. // MP4AudioSampleEntry, ISO/IEC 14496-14
  5844. ]), esds(track));
  5845. };
  5846. }());
  5847. tkhd = function(track) {
  5848. var result = new Uint8Array([
  5849. 0x00, // version 0
  5850. 0x00, 0x00, 0x07, // flags
  5851. 0x00, 0x00, 0x00, 0x00, // creation_time
  5852. 0x00, 0x00, 0x00, 0x00, // modification_time
  5853. (track.id & 0xFF000000) >> 24,
  5854. (track.id & 0xFF0000) >> 16,
  5855. (track.id & 0xFF00) >> 8,
  5856. track.id & 0xFF, // track_ID
  5857. 0x00, 0x00, 0x00, 0x00, // reserved
  5858. (track.duration & 0xFF000000) >> 24,
  5859. (track.duration & 0xFF0000) >> 16,
  5860. (track.duration & 0xFF00) >> 8,
  5861. track.duration & 0xFF, // duration
  5862. 0x00, 0x00, 0x00, 0x00,
  5863. 0x00, 0x00, 0x00, 0x00, // reserved
  5864. 0x00, 0x00, // layer
  5865. 0x00, 0x00, // alternate_group
  5866. 0x01, 0x00, // non-audio track volume
  5867. 0x00, 0x00, // reserved
  5868. 0x00, 0x01, 0x00, 0x00,
  5869. 0x00, 0x00, 0x00, 0x00,
  5870. 0x00, 0x00, 0x00, 0x00,
  5871. 0x00, 0x00, 0x00, 0x00,
  5872. 0x00, 0x01, 0x00, 0x00,
  5873. 0x00, 0x00, 0x00, 0x00,
  5874. 0x00, 0x00, 0x00, 0x00,
  5875. 0x00, 0x00, 0x00, 0x00,
  5876. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  5877. (track.width & 0xFF00) >> 8,
  5878. track.width & 0xFF,
  5879. 0x00, 0x00, // width
  5880. (track.height & 0xFF00) >> 8,
  5881. track.height & 0xFF,
  5882. 0x00, 0x00 // height
  5883. ]);
  5884. return box(types.tkhd, result);
  5885. };
  5886. /**
  5887. * Generate a track fragment (traf) box. A traf box collects metadata
  5888. * about tracks in a movie fragment (moof) box.
  5889. */
  5890. traf = function(track) {
  5891. var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
  5892. sampleDependencyTable, dataOffset,
  5893. upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  5894. trackFragmentHeader = box(types.tfhd, new Uint8Array([
  5895. 0x00, // version 0
  5896. 0x00, 0x00, 0x3a, // flags
  5897. (track.id & 0xFF000000) >> 24,
  5898. (track.id & 0xFF0000) >> 16,
  5899. (track.id & 0xFF00) >> 8,
  5900. (track.id & 0xFF), // track_ID
  5901. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  5902. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  5903. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  5904. 0x00, 0x00, 0x00, 0x00 // default_sample_flags
  5905. ]));
  5906. upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  5907. lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  5908. trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
  5909. 0x01, // version 1
  5910. 0x00, 0x00, 0x00, // flags
  5911. // baseMediaDecodeTime
  5912. (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
  5913. (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
  5914. (upperWordBaseMediaDecodeTime >>> 8) & 0xFF,
  5915. upperWordBaseMediaDecodeTime & 0xFF,
  5916. (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
  5917. (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
  5918. (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF,
  5919. lowerWordBaseMediaDecodeTime & 0xFF
  5920. ]));
  5921. // the data offset specifies the number of bytes from the start of
  5922. // the containing moof to the first payload byte of the associated
  5923. // mdat
  5924. dataOffset = (32 + // tfhd
  5925. 20 + // tfdt
  5926. 8 + // traf header
  5927. 16 + // mfhd
  5928. 8 + // moof header
  5929. 8); // mdat header
  5930. // audio tracks require less metadata
  5931. if (track.type === 'audio') {
  5932. trackFragmentRun = trun(track, dataOffset);
  5933. return box(types.traf,
  5934. trackFragmentHeader,
  5935. trackFragmentDecodeTime,
  5936. trackFragmentRun);
  5937. }
  5938. // video tracks should contain an independent and disposable samples
  5939. // box (sdtp)
  5940. // generate one and adjust offsets to match
  5941. sampleDependencyTable = sdtp(track);
  5942. trackFragmentRun = trun(track,
  5943. sampleDependencyTable.length + dataOffset);
  5944. return box(types.traf,
  5945. trackFragmentHeader,
  5946. trackFragmentDecodeTime,
  5947. trackFragmentRun,
  5948. sampleDependencyTable);
  5949. };
  5950. /**
  5951. * Generate a track box.
  5952. * @param track {object} a track definition
  5953. * @return {Uint8Array} the track box
  5954. */
  5955. trak = function(track) {
  5956. track.duration = track.duration || 0xffffffff;
  5957. return box(types.trak,
  5958. tkhd(track),
  5959. mdia(track));
  5960. };
  5961. trex = function(track) {
  5962. var result = new Uint8Array([
  5963. 0x00, // version 0
  5964. 0x00, 0x00, 0x00, // flags
  5965. (track.id & 0xFF000000) >> 24,
  5966. (track.id & 0xFF0000) >> 16,
  5967. (track.id & 0xFF00) >> 8,
  5968. (track.id & 0xFF), // track_ID
  5969. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  5970. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  5971. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  5972. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  5973. ]);
  5974. // the last two bytes of default_sample_flags is the sample
  5975. // degradation priority, a hint about the importance of this sample
  5976. // relative to others. Lower the degradation priority for all sample
  5977. // types other than video.
  5978. if (track.type !== 'video') {
  5979. result[result.length - 1] = 0x00;
  5980. }
  5981. return box(types.trex, result);
  5982. };
  5983. (function() {
  5984. var audioTrun, videoTrun, trunHeader;
  5985. // This method assumes all samples are uniform. That is, if a
  5986. // duration is present for the first sample, it will be present for
  5987. // all subsequent samples.
  5988. // see ISO/IEC 14496-12:2012, Section 8.8.8.1
  5989. trunHeader = function(samples, offset) {
  5990. var durationPresent = 0, sizePresent = 0,
  5991. flagsPresent = 0, compositionTimeOffset = 0;
  5992. // trun flag constants
  5993. if (samples.length) {
  5994. if (samples[0].duration !== undefined) {
  5995. durationPresent = 0x1;
  5996. }
  5997. if (samples[0].size !== undefined) {
  5998. sizePresent = 0x2;
  5999. }
  6000. if (samples[0].flags !== undefined) {
  6001. flagsPresent = 0x4;
  6002. }
  6003. if (samples[0].compositionTimeOffset !== undefined) {
  6004. compositionTimeOffset = 0x8;
  6005. }
  6006. }
  6007. return [
  6008. 0x00, // version 0
  6009. 0x00,
  6010. durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
  6011. 0x01, // flags
  6012. (samples.length & 0xFF000000) >>> 24,
  6013. (samples.length & 0xFF0000) >>> 16,
  6014. (samples.length & 0xFF00) >>> 8,
  6015. samples.length & 0xFF, // sample_count
  6016. (offset & 0xFF000000) >>> 24,
  6017. (offset & 0xFF0000) >>> 16,
  6018. (offset & 0xFF00) >>> 8,
  6019. offset & 0xFF // data_offset
  6020. ];
  6021. };
  6022. videoTrun = function(track, offset) {
  6023. var bytes, samples, sample, i;
  6024. samples = track.samples || [];
  6025. offset += 8 + 12 + (16 * samples.length);
  6026. bytes = trunHeader(samples, offset);
  6027. for (i = 0; i < samples.length; i++) {
  6028. sample = samples[i];
  6029. bytes = bytes.concat([
  6030. (sample.duration & 0xFF000000) >>> 24,
  6031. (sample.duration & 0xFF0000) >>> 16,
  6032. (sample.duration & 0xFF00) >>> 8,
  6033. sample.duration & 0xFF, // sample_duration
  6034. (sample.size & 0xFF000000) >>> 24,
  6035. (sample.size & 0xFF0000) >>> 16,
  6036. (sample.size & 0xFF00) >>> 8,
  6037. sample.size & 0xFF, // sample_size
  6038. (sample.flags.isLeading << 2) | sample.flags.dependsOn,
  6039. (sample.flags.isDependedOn << 6) |
  6040. (sample.flags.hasRedundancy << 4) |
  6041. (sample.flags.paddingValue << 1) |
  6042. sample.flags.isNonSyncSample,
  6043. sample.flags.degradationPriority & 0xF0 << 8,
  6044. sample.flags.degradationPriority & 0x0F, // sample_flags
  6045. (sample.compositionTimeOffset & 0xFF000000) >>> 24,
  6046. (sample.compositionTimeOffset & 0xFF0000) >>> 16,
  6047. (sample.compositionTimeOffset & 0xFF00) >>> 8,
  6048. sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
  6049. ]);
  6050. }
  6051. return box(types.trun, new Uint8Array(bytes));
  6052. };
  6053. audioTrun = function(track, offset) {
  6054. var bytes, samples, sample, i;
  6055. samples = track.samples || [];
  6056. offset += 8 + 12 + (8 * samples.length);
  6057. bytes = trunHeader(samples, offset);
  6058. for (i = 0; i < samples.length; i++) {
  6059. sample = samples[i];
  6060. bytes = bytes.concat([
  6061. (sample.duration & 0xFF000000) >>> 24,
  6062. (sample.duration & 0xFF0000) >>> 16,
  6063. (sample.duration & 0xFF00) >>> 8,
  6064. sample.duration & 0xFF, // sample_duration
  6065. (sample.size & 0xFF000000) >>> 24,
  6066. (sample.size & 0xFF0000) >>> 16,
  6067. (sample.size & 0xFF00) >>> 8,
  6068. sample.size & 0xFF]); // sample_size
  6069. }
  6070. return box(types.trun, new Uint8Array(bytes));
  6071. };
  6072. trun = function(track, offset) {
  6073. if (track.type === 'audio') {
  6074. return audioTrun(track, offset);
  6075. }
  6076. return videoTrun(track, offset);
  6077. };
  6078. }());
  6079. module.exports = {
  6080. ftyp: ftyp,
  6081. mdat: mdat,
  6082. moof: moof,
  6083. moov: moov,
  6084. initSegment: function(tracks) {
  6085. var
  6086. fileType = ftyp(),
  6087. movie = moov(tracks),
  6088. result;
  6089. result = new Uint8Array(fileType.byteLength + movie.byteLength);
  6090. result.set(fileType);
  6091. result.set(movie, fileType.byteLength);
  6092. return result;
  6093. }
  6094. };
  6095. },{}],32:[function(require,module,exports){
  6096. /**
  6097. * mux.js
  6098. *
  6099. * Copyright (c) 2015 Brightcove
  6100. * All rights reserved.
  6101. *
  6102. * A stream-based mp2t to mp4 converter. This utility can be used to
  6103. * deliver mp4s to a SourceBuffer on platforms that support native
  6104. * Media Source Extensions.
  6105. */
  6106. 'use strict';
  6107. var Stream = require('../utils/stream.js');
  6108. var mp4 = require('./mp4-generator.js');
  6109. var m2ts = require('../m2ts/m2ts.js');
  6110. var AdtsStream = require('../codecs/adts.js');
  6111. var H264Stream = require('../codecs/h264').H264Stream;
  6112. var AacStream = require('../aac');
  6113. var coneOfSilence = require('../data/silence');
  6114. var clock = require('../utils/clock');
  6115. // constants
  6116. var AUDIO_PROPERTIES = [
  6117. 'audioobjecttype',
  6118. 'channelcount',
  6119. 'samplerate',
  6120. 'samplingfrequencyindex',
  6121. 'samplesize'
  6122. ];
  6123. var VIDEO_PROPERTIES = [
  6124. 'width',
  6125. 'height',
  6126. 'profileIdc',
  6127. 'levelIdc',
  6128. 'profileCompatibility'
  6129. ];
  6130. var ONE_SECOND_IN_TS = 90000; // 90kHz clock
  6131. // object types
  6132. var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
  6133. // Helper functions
  6134. var
  6135. createDefaultSample,
  6136. isLikelyAacData,
  6137. collectDtsInfo,
  6138. clearDtsInfo,
  6139. calculateTrackBaseMediaDecodeTime,
  6140. arrayEquals,
  6141. sumFrameByteLengths;
  6142. /**
  6143. * Default sample object
  6144. * see ISO/IEC 14496-12:2012, section 8.6.4.3
  6145. */
  6146. createDefaultSample = function() {
  6147. return {
  6148. size: 0,
  6149. flags: {
  6150. isLeading: 0,
  6151. dependsOn: 1,
  6152. isDependedOn: 0,
  6153. hasRedundancy: 0,
  6154. degradationPriority: 0
  6155. }
  6156. };
  6157. };
  6158. isLikelyAacData = function(data) {
  6159. if ((data[0] === 'I'.charCodeAt(0)) &&
  6160. (data[1] === 'D'.charCodeAt(0)) &&
  6161. (data[2] === '3'.charCodeAt(0))) {
  6162. return true;
  6163. }
  6164. return false;
  6165. };
  6166. /**
  6167. * Compare two arrays (even typed) for same-ness
  6168. */
  6169. arrayEquals = function(a, b) {
  6170. var
  6171. i;
  6172. if (a.length !== b.length) {
  6173. return false;
  6174. }
  6175. // compare the value of each element in the array
  6176. for (i = 0; i < a.length; i++) {
  6177. if (a[i] !== b[i]) {
  6178. return false;
  6179. }
  6180. }
  6181. return true;
  6182. };
  6183. /**
  6184. * Sum the `byteLength` properties of the data in each AAC frame
  6185. */
  6186. sumFrameByteLengths = function(array) {
  6187. var
  6188. i,
  6189. currentObj,
  6190. sum = 0;
  6191. // sum the byteLength's all each nal unit in the frame
  6192. for (i = 0; i < array.length; i++) {
  6193. currentObj = array[i];
  6194. sum += currentObj.data.byteLength;
  6195. }
  6196. return sum;
  6197. };
  6198. /**
  6199. * Constructs a single-track, ISO BMFF media segment from AAC data
  6200. * events. The output of this stream can be fed to a SourceBuffer
  6201. * configured with a suitable initialization segment.
  6202. */
  6203. AudioSegmentStream = function(track) {
  6204. var
  6205. adtsFrames = [],
  6206. sequenceNumber = 0,
  6207. earliestAllowedDts = 0,
  6208. audioAppendStartTs = 0,
  6209. videoBaseMediaDecodeTime = Infinity;
  6210. AudioSegmentStream.prototype.init.call(this);
  6211. this.push = function(data) {
  6212. collectDtsInfo(track, data);
  6213. if (track) {
  6214. AUDIO_PROPERTIES.forEach(function(prop) {
  6215. track[prop] = data[prop];
  6216. });
  6217. }
  6218. // buffer audio data until end() is called
  6219. adtsFrames.push(data);
  6220. };
  6221. this.setEarliestDts = function(earliestDts) {
  6222. earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  6223. };
  6224. this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  6225. videoBaseMediaDecodeTime = baseMediaDecodeTime;
  6226. };
  6227. this.setAudioAppendStart = function(timestamp) {
  6228. audioAppendStartTs = timestamp;
  6229. };
  6230. this.flush = function() {
  6231. var
  6232. frames,
  6233. moof,
  6234. mdat,
  6235. boxes;
  6236. // return early if no audio data has been observed
  6237. if (adtsFrames.length === 0) {
  6238. this.trigger('done', 'AudioSegmentStream');
  6239. return;
  6240. }
  6241. frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
  6242. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  6243. this.prefixWithSilence_(track, frames);
  6244. // we have to build the index from byte locations to
  6245. // samples (that is, adts frames) in the audio data
  6246. track.samples = this.generateSampleTable_(frames);
  6247. // concatenate the audio data to constuct the mdat
  6248. mdat = mp4.mdat(this.concatenateFrameData_(frames));
  6249. adtsFrames = [];
  6250. moof = mp4.moof(sequenceNumber, [track]);
  6251. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  6252. // bump the sequence number for next time
  6253. sequenceNumber++;
  6254. boxes.set(moof);
  6255. boxes.set(mdat, moof.byteLength);
  6256. clearDtsInfo(track);
  6257. this.trigger('data', {track: track, boxes: boxes});
  6258. this.trigger('done', 'AudioSegmentStream');
  6259. };
  6260. // Possibly pad (prefix) the audio track with silence if appending this track
  6261. // would lead to the introduction of a gap in the audio buffer
  6262. this.prefixWithSilence_ = function(track, frames) {
  6263. var
  6264. baseMediaDecodeTimeTs,
  6265. frameDuration = 0,
  6266. audioGapDuration = 0,
  6267. audioFillFrameCount = 0,
  6268. audioFillDuration = 0,
  6269. silentFrame,
  6270. i;
  6271. if (!frames.length) {
  6272. return;
  6273. }
  6274. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
  6275. // determine frame clock duration based on sample rate, round up to avoid overfills
  6276. frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
  6277. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  6278. // insert the shortest possible amount (audio gap or audio to video gap)
  6279. audioGapDuration =
  6280. baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
  6281. // number of full frames in the audio gap
  6282. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  6283. audioFillDuration = audioFillFrameCount * frameDuration;
  6284. }
  6285. // don't attempt to fill gaps smaller than a single frame or larger
  6286. // than a half second
  6287. if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
  6288. return;
  6289. }
  6290. silentFrame = coneOfSilence[track.samplerate];
  6291. if (!silentFrame) {
  6292. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  6293. // from the content instead
  6294. silentFrame = frames[0].data;
  6295. }
  6296. for (i = 0; i < audioFillFrameCount; i++) {
  6297. frames.splice(i, 0, {
  6298. data: silentFrame
  6299. });
  6300. }
  6301. track.baseMediaDecodeTime -=
  6302. Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  6303. };
  6304. // If the audio segment extends before the earliest allowed dts
  6305. // value, remove AAC frames until starts at or after the earliest
  6306. // allowed DTS so that we don't end up with a negative baseMedia-
  6307. // DecodeTime for the audio track
  6308. this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
  6309. if (track.minSegmentDts >= earliestAllowedDts) {
  6310. return adtsFrames;
  6311. }
  6312. // We will need to recalculate the earliest segment Dts
  6313. track.minSegmentDts = Infinity;
  6314. return adtsFrames.filter(function(currentFrame) {
  6315. // If this is an allowed frame, keep it and record it's Dts
  6316. if (currentFrame.dts >= earliestAllowedDts) {
  6317. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  6318. track.minSegmentPts = track.minSegmentDts;
  6319. return true;
  6320. }
  6321. // Otherwise, discard it
  6322. return false;
  6323. });
  6324. };
  6325. // generate the track's raw mdat data from an array of frames
  6326. this.generateSampleTable_ = function(frames) {
  6327. var
  6328. i,
  6329. currentFrame,
  6330. samples = [];
  6331. for (i = 0; i < frames.length; i++) {
  6332. currentFrame = frames[i];
  6333. samples.push({
  6334. size: currentFrame.data.byteLength,
  6335. duration: 1024 // For AAC audio, all samples contain 1024 samples
  6336. });
  6337. }
  6338. return samples;
  6339. };
  6340. // generate the track's sample table from an array of frames
  6341. this.concatenateFrameData_ = function(frames) {
  6342. var
  6343. i,
  6344. currentFrame,
  6345. dataOffset = 0,
  6346. data = new Uint8Array(sumFrameByteLengths(frames));
  6347. for (i = 0; i < frames.length; i++) {
  6348. currentFrame = frames[i];
  6349. data.set(currentFrame.data, dataOffset);
  6350. dataOffset += currentFrame.data.byteLength;
  6351. }
  6352. return data;
  6353. };
  6354. };
  6355. AudioSegmentStream.prototype = new Stream();
  6356. /**
  6357. * Constructs a single-track, ISO BMFF media segment from H264 data
  6358. * events. The output of this stream can be fed to a SourceBuffer
  6359. * configured with a suitable initialization segment.
  6360. * @param track {object} track metadata configuration
  6361. * @param options {object} transmuxer options object
  6362. * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
  6363. * gopsToAlignWith list when attempting to align gop pts
  6364. */
  6365. VideoSegmentStream = function(track, options) {
  6366. var
  6367. sequenceNumber = 0,
  6368. nalUnits = [],
  6369. gopsToAlignWith = [],
  6370. config,
  6371. pps;
  6372. options = options || {};
  6373. VideoSegmentStream.prototype.init.call(this);
  6374. delete track.minPTS;
  6375. this.gopCache_ = [];
  6376. this.push = function(nalUnit) {
  6377. collectDtsInfo(track, nalUnit);
  6378. // record the track config
  6379. if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
  6380. config = nalUnit.config;
  6381. track.sps = [nalUnit.data];
  6382. VIDEO_PROPERTIES.forEach(function(prop) {
  6383. track[prop] = config[prop];
  6384. }, this);
  6385. }
  6386. if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
  6387. !pps) {
  6388. pps = nalUnit.data;
  6389. track.pps = [nalUnit.data];
  6390. }
  6391. // buffer video until flush() is called
  6392. nalUnits.push(nalUnit);
  6393. };
  6394. this.flush = function() {
  6395. var
  6396. frames,
  6397. gopForFusion,
  6398. gops,
  6399. moof,
  6400. mdat,
  6401. boxes;
  6402. // Throw away nalUnits at the start of the byte stream until
  6403. // we find the first AUD
  6404. while (nalUnits.length) {
  6405. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  6406. break;
  6407. }
  6408. nalUnits.shift();
  6409. }
  6410. // Return early if no video data has been observed
  6411. if (nalUnits.length === 0) {
  6412. this.resetStream_();
  6413. this.trigger('done', 'VideoSegmentStream');
  6414. return;
  6415. }
  6416. // Organize the raw nal-units into arrays that represent
  6417. // higher-level constructs such as frames and gops
  6418. // (group-of-pictures)
  6419. frames = this.groupNalsIntoFrames_(nalUnits);
  6420. gops = this.groupFramesIntoGops_(frames);
  6421. // If the first frame of this fragment is not a keyframe we have
  6422. // a problem since MSE (on Chrome) requires a leading keyframe.
  6423. //
  6424. // We have two approaches to repairing this situation:
  6425. // 1) GOP-FUSION:
  6426. // This is where we keep track of the GOPS (group-of-pictures)
  6427. // from previous fragments and attempt to find one that we can
  6428. // prepend to the current fragment in order to create a valid
  6429. // fragment.
  6430. // 2) KEYFRAME-PULLING:
  6431. // Here we search for the first keyframe in the fragment and
  6432. // throw away all the frames between the start of the fragment
  6433. // and that keyframe. We then extend the duration and pull the
  6434. // PTS of the keyframe forward so that it covers the time range
  6435. // of the frames that were disposed of.
  6436. //
  6437. // #1 is far prefereable over #2 which can cause "stuttering" but
  6438. // requires more things to be just right.
  6439. if (!gops[0][0].keyFrame) {
  6440. // Search for a gop for fusion from our gopCache
  6441. gopForFusion = this.getGopForFusion_(nalUnits[0], track);
  6442. if (gopForFusion) {
  6443. gops.unshift(gopForFusion);
  6444. // Adjust Gops' metadata to account for the inclusion of the
  6445. // new gop at the beginning
  6446. gops.byteLength += gopForFusion.byteLength;
  6447. gops.nalCount += gopForFusion.nalCount;
  6448. gops.pts = gopForFusion.pts;
  6449. gops.dts = gopForFusion.dts;
  6450. gops.duration += gopForFusion.duration;
  6451. } else {
  6452. // If we didn't find a candidate gop fall back to keyrame-pulling
  6453. gops = this.extendFirstKeyFrame_(gops);
  6454. }
  6455. }
  6456. // Trim gops to align with gopsToAlignWith
  6457. if (gopsToAlignWith.length) {
  6458. var alignedGops;
  6459. if (options.alignGopsAtEnd) {
  6460. alignedGops = this.alignGopsAtEnd_(gops);
  6461. } else {
  6462. alignedGops = this.alignGopsAtStart_(gops);
  6463. }
  6464. if (!alignedGops) {
  6465. // save all the nals in the last GOP into the gop cache
  6466. this.gopCache_.unshift({
  6467. gop: gops.pop(),
  6468. pps: track.pps,
  6469. sps: track.sps
  6470. });
  6471. // Keep a maximum of 6 GOPs in the cache
  6472. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  6473. // Clear nalUnits
  6474. nalUnits = [];
  6475. // return early no gops can be aligned with desired gopsToAlignWith
  6476. this.resetStream_();
  6477. this.trigger('done', 'VideoSegmentStream');
  6478. return;
  6479. }
  6480. // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
  6481. // when recalculated before sending off to CoalesceStream
  6482. clearDtsInfo(track);
  6483. gops = alignedGops;
  6484. }
  6485. collectDtsInfo(track, gops);
  6486. // First, we have to build the index from byte locations to
  6487. // samples (that is, frames) in the video data
  6488. track.samples = this.generateSampleTable_(gops);
  6489. // Concatenate the video data and construct the mdat
  6490. mdat = mp4.mdat(this.concatenateNalData_(gops));
  6491. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  6492. this.trigger('processedGopsInfo', gops.map(function(gop) {
  6493. return {
  6494. pts: gop.pts,
  6495. dts: gop.dts,
  6496. byteLength: gop.byteLength
  6497. };
  6498. }));
  6499. // save all the nals in the last GOP into the gop cache
  6500. this.gopCache_.unshift({
  6501. gop: gops.pop(),
  6502. pps: track.pps,
  6503. sps: track.sps
  6504. });
  6505. // Keep a maximum of 6 GOPs in the cache
  6506. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  6507. // Clear nalUnits
  6508. nalUnits = [];
  6509. this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
  6510. this.trigger('timelineStartInfo', track.timelineStartInfo);
  6511. moof = mp4.moof(sequenceNumber, [track]);
  6512. // it would be great to allocate this array up front instead of
  6513. // throwing away hundreds of media segment fragments
  6514. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  6515. // Bump the sequence number for next time
  6516. sequenceNumber++;
  6517. boxes.set(moof);
  6518. boxes.set(mdat, moof.byteLength);
  6519. this.trigger('data', {track: track, boxes: boxes});
  6520. this.resetStream_();
  6521. // Continue with the flush process now
  6522. this.trigger('done', 'VideoSegmentStream');
  6523. };
  6524. this.resetStream_ = function() {
  6525. clearDtsInfo(track);
  6526. // reset config and pps because they may differ across segments
  6527. // for instance, when we are rendition switching
  6528. config = undefined;
  6529. pps = undefined;
  6530. };
  6531. // Search for a candidate Gop for gop-fusion from the gop cache and
  6532. // return it or return null if no good candidate was found
  6533. this.getGopForFusion_ = function(nalUnit) {
  6534. var
  6535. halfSecond = 45000, // Half-a-second in a 90khz clock
  6536. allowableOverlap = 10000, // About 3 frames @ 30fps
  6537. nearestDistance = Infinity,
  6538. dtsDistance,
  6539. nearestGopObj,
  6540. currentGop,
  6541. currentGopObj,
  6542. i;
  6543. // Search for the GOP nearest to the beginning of this nal unit
  6544. for (i = 0; i < this.gopCache_.length; i++) {
  6545. currentGopObj = this.gopCache_[i];
  6546. currentGop = currentGopObj.gop;
  6547. // Reject Gops with different SPS or PPS
  6548. if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
  6549. !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
  6550. continue;
  6551. }
  6552. // Reject Gops that would require a negative baseMediaDecodeTime
  6553. if (currentGop.dts < track.timelineStartInfo.dts) {
  6554. continue;
  6555. }
  6556. // The distance between the end of the gop and the start of the nalUnit
  6557. dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
  6558. // Only consider GOPS that start before the nal unit and end within
  6559. // a half-second of the nal unit
  6560. if (dtsDistance >= -allowableOverlap &&
  6561. dtsDistance <= halfSecond) {
  6562. // Always use the closest GOP we found if there is more than
  6563. // one candidate
  6564. if (!nearestGopObj ||
  6565. nearestDistance > dtsDistance) {
  6566. nearestGopObj = currentGopObj;
  6567. nearestDistance = dtsDistance;
  6568. }
  6569. }
  6570. }
  6571. if (nearestGopObj) {
  6572. return nearestGopObj.gop;
  6573. }
  6574. return null;
  6575. };
  6576. this.extendFirstKeyFrame_ = function(gops) {
  6577. var currentGop;
  6578. if (!gops[0][0].keyFrame && gops.length > 1) {
  6579. // Remove the first GOP
  6580. currentGop = gops.shift();
  6581. gops.byteLength -= currentGop.byteLength;
  6582. gops.nalCount -= currentGop.nalCount;
  6583. // Extend the first frame of what is now the
  6584. // first gop to cover the time period of the
  6585. // frames we just removed
  6586. gops[0][0].dts = currentGop.dts;
  6587. gops[0][0].pts = currentGop.pts;
  6588. gops[0][0].duration += currentGop.duration;
  6589. }
  6590. return gops;
  6591. };
  6592. // Convert an array of nal units into an array of frames with each frame being
  6593. // composed of the nal units that make up that frame
  6594. // Also keep track of cummulative data about the frame from the nal units such
  6595. // as the frame duration, starting pts, etc.
  6596. this.groupNalsIntoFrames_ = function(nalUnits) {
  6597. var
  6598. i,
  6599. currentNal,
  6600. currentFrame = [],
  6601. frames = [];
  6602. currentFrame.byteLength = 0;
  6603. for (i = 0; i < nalUnits.length; i++) {
  6604. currentNal = nalUnits[i];
  6605. // Split on 'aud'-type nal units
  6606. if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  6607. // Since the very first nal unit is expected to be an AUD
  6608. // only push to the frames array when currentFrame is not empty
  6609. if (currentFrame.length) {
  6610. currentFrame.duration = currentNal.dts - currentFrame.dts;
  6611. frames.push(currentFrame);
  6612. }
  6613. currentFrame = [currentNal];
  6614. currentFrame.byteLength = currentNal.data.byteLength;
  6615. currentFrame.pts = currentNal.pts;
  6616. currentFrame.dts = currentNal.dts;
  6617. } else {
  6618. // Specifically flag key frames for ease of use later
  6619. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  6620. currentFrame.keyFrame = true;
  6621. }
  6622. currentFrame.duration = currentNal.dts - currentFrame.dts;
  6623. currentFrame.byteLength += currentNal.data.byteLength;
  6624. currentFrame.push(currentNal);
  6625. }
  6626. }
  6627. // For the last frame, use the duration of the previous frame if we
  6628. // have nothing better to go on
  6629. if (frames.length &&
  6630. (!currentFrame.duration ||
  6631. currentFrame.duration <= 0)) {
  6632. currentFrame.duration = frames[frames.length - 1].duration;
  6633. }
  6634. // Push the final frame
  6635. frames.push(currentFrame);
  6636. return frames;
  6637. };
  6638. // Convert an array of frames into an array of Gop with each Gop being composed
  6639. // of the frames that make up that Gop
  6640. // Also keep track of cummulative data about the Gop from the frames such as the
  6641. // Gop duration, starting pts, etc.
  6642. this.groupFramesIntoGops_ = function(frames) {
  6643. var
  6644. i,
  6645. currentFrame,
  6646. currentGop = [],
  6647. gops = [];
  6648. // We must pre-set some of the values on the Gop since we
  6649. // keep running totals of these values
  6650. currentGop.byteLength = 0;
  6651. currentGop.nalCount = 0;
  6652. currentGop.duration = 0;
  6653. currentGop.pts = frames[0].pts;
  6654. currentGop.dts = frames[0].dts;
  6655. // store some metadata about all the Gops
  6656. gops.byteLength = 0;
  6657. gops.nalCount = 0;
  6658. gops.duration = 0;
  6659. gops.pts = frames[0].pts;
  6660. gops.dts = frames[0].dts;
  6661. for (i = 0; i < frames.length; i++) {
  6662. currentFrame = frames[i];
  6663. if (currentFrame.keyFrame) {
  6664. // Since the very first frame is expected to be an keyframe
  6665. // only push to the gops array when currentGop is not empty
  6666. if (currentGop.length) {
  6667. gops.push(currentGop);
  6668. gops.byteLength += currentGop.byteLength;
  6669. gops.nalCount += currentGop.nalCount;
  6670. gops.duration += currentGop.duration;
  6671. }
  6672. currentGop = [currentFrame];
  6673. currentGop.nalCount = currentFrame.length;
  6674. currentGop.byteLength = currentFrame.byteLength;
  6675. currentGop.pts = currentFrame.pts;
  6676. currentGop.dts = currentFrame.dts;
  6677. currentGop.duration = currentFrame.duration;
  6678. } else {
  6679. currentGop.duration += currentFrame.duration;
  6680. currentGop.nalCount += currentFrame.length;
  6681. currentGop.byteLength += currentFrame.byteLength;
  6682. currentGop.push(currentFrame);
  6683. }
  6684. }
  6685. if (gops.length && currentGop.duration <= 0) {
  6686. currentGop.duration = gops[gops.length - 1].duration;
  6687. }
  6688. gops.byteLength += currentGop.byteLength;
  6689. gops.nalCount += currentGop.nalCount;
  6690. gops.duration += currentGop.duration;
  6691. // push the final Gop
  6692. gops.push(currentGop);
  6693. return gops;
  6694. };
  6695. // generate the track's sample table from an array of gops
  6696. this.generateSampleTable_ = function(gops, baseDataOffset) {
  6697. var
  6698. h, i,
  6699. sample,
  6700. currentGop,
  6701. currentFrame,
  6702. dataOffset = baseDataOffset || 0,
  6703. samples = [];
  6704. for (h = 0; h < gops.length; h++) {
  6705. currentGop = gops[h];
  6706. for (i = 0; i < currentGop.length; i++) {
  6707. currentFrame = currentGop[i];
  6708. sample = createDefaultSample();
  6709. sample.dataOffset = dataOffset;
  6710. sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
  6711. sample.duration = currentFrame.duration;
  6712. sample.size = 4 * currentFrame.length; // Space for nal unit size
  6713. sample.size += currentFrame.byteLength;
  6714. if (currentFrame.keyFrame) {
  6715. sample.flags.dependsOn = 2;
  6716. }
  6717. dataOffset += sample.size;
  6718. samples.push(sample);
  6719. }
  6720. }
  6721. return samples;
  6722. };
  6723. // generate the track's raw mdat data from an array of gops
  6724. this.concatenateNalData_ = function(gops) {
  6725. var
  6726. h, i, j,
  6727. currentGop,
  6728. currentFrame,
  6729. currentNal,
  6730. dataOffset = 0,
  6731. nalsByteLength = gops.byteLength,
  6732. numberOfNals = gops.nalCount,
  6733. totalByteLength = nalsByteLength + 4 * numberOfNals,
  6734. data = new Uint8Array(totalByteLength),
  6735. view = new DataView(data.buffer);
  6736. // For each Gop..
  6737. for (h = 0; h < gops.length; h++) {
  6738. currentGop = gops[h];
  6739. // For each Frame..
  6740. for (i = 0; i < currentGop.length; i++) {
  6741. currentFrame = currentGop[i];
  6742. // For each NAL..
  6743. for (j = 0; j < currentFrame.length; j++) {
  6744. currentNal = currentFrame[j];
  6745. view.setUint32(dataOffset, currentNal.data.byteLength);
  6746. dataOffset += 4;
  6747. data.set(currentNal.data, dataOffset);
  6748. dataOffset += currentNal.data.byteLength;
  6749. }
  6750. }
  6751. }
  6752. return data;
  6753. };
  6754. // trim gop list to the first gop found that has a matching pts with a gop in the list
  6755. // of gopsToAlignWith starting from the START of the list
  6756. this.alignGopsAtStart_ = function(gops) {
  6757. var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
  6758. byteLength = gops.byteLength;
  6759. nalCount = gops.nalCount;
  6760. duration = gops.duration;
  6761. alignIndex = gopIndex = 0;
  6762. while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
  6763. align = gopsToAlignWith[alignIndex];
  6764. gop = gops[gopIndex];
  6765. if (align.pts === gop.pts) {
  6766. break;
  6767. }
  6768. if (gop.pts > align.pts) {
  6769. // this current gop starts after the current gop we want to align on, so increment
  6770. // align index
  6771. alignIndex++;
  6772. continue;
  6773. }
  6774. // current gop starts before the current gop we want to align on. so increment gop
  6775. // index
  6776. gopIndex++;
  6777. byteLength -= gop.byteLength;
  6778. nalCount -= gop.nalCount;
  6779. duration -= gop.duration;
  6780. }
  6781. if (gopIndex === 0) {
  6782. // no gops to trim
  6783. return gops;
  6784. }
  6785. if (gopIndex === gops.length) {
  6786. // all gops trimmed, skip appending all gops
  6787. return null;
  6788. }
  6789. alignedGops = gops.slice(gopIndex);
  6790. alignedGops.byteLength = byteLength;
  6791. alignedGops.duration = duration;
  6792. alignedGops.nalCount = nalCount;
  6793. alignedGops.pts = alignedGops[0].pts;
  6794. alignedGops.dts = alignedGops[0].dts;
  6795. return alignedGops;
  6796. };
  6797. // trim gop list to the first gop found that has a matching pts with a gop in the list
  6798. // of gopsToAlignWith starting from the END of the list
  6799. this.alignGopsAtEnd_ = function(gops) {
  6800. var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
  6801. alignIndex = gopsToAlignWith.length - 1;
  6802. gopIndex = gops.length - 1;
  6803. alignEndIndex = null;
  6804. matchFound = false;
  6805. while (alignIndex >= 0 && gopIndex >= 0) {
  6806. align = gopsToAlignWith[alignIndex];
  6807. gop = gops[gopIndex];
  6808. if (align.pts === gop.pts) {
  6809. matchFound = true;
  6810. break;
  6811. }
  6812. if (align.pts > gop.pts) {
  6813. alignIndex--;
  6814. continue;
  6815. }
  6816. if (alignIndex === gopsToAlignWith.length - 1) {
  6817. // gop.pts is greater than the last alignment candidate. If no match is found
  6818. // by the end of this loop, we still want to append gops that come after this
  6819. // point
  6820. alignEndIndex = gopIndex;
  6821. }
  6822. gopIndex--;
  6823. }
  6824. if (!matchFound && alignEndIndex === null) {
  6825. return null;
  6826. }
  6827. var trimIndex;
  6828. if (matchFound) {
  6829. trimIndex = gopIndex;
  6830. } else {
  6831. trimIndex = alignEndIndex;
  6832. }
  6833. if (trimIndex === 0) {
  6834. return gops;
  6835. }
  6836. var alignedGops = gops.slice(trimIndex);
  6837. var metadata = alignedGops.reduce(function(total, gop) {
  6838. total.byteLength += gop.byteLength;
  6839. total.duration += gop.duration;
  6840. total.nalCount += gop.nalCount;
  6841. return total;
  6842. }, { byteLength: 0, duration: 0, nalCount: 0 });
  6843. alignedGops.byteLength = metadata.byteLength;
  6844. alignedGops.duration = metadata.duration;
  6845. alignedGops.nalCount = metadata.nalCount;
  6846. alignedGops.pts = alignedGops[0].pts;
  6847. alignedGops.dts = alignedGops[0].dts;
  6848. return alignedGops;
  6849. };
  6850. this.alignGopsWith = function(newGopsToAlignWith) {
  6851. gopsToAlignWith = newGopsToAlignWith;
  6852. };
  6853. };
  6854. VideoSegmentStream.prototype = new Stream();
  6855. /**
  6856. * Store information about the start and end of the track and the
  6857. * duration for each frame/sample we process in order to calculate
  6858. * the baseMediaDecodeTime
  6859. */
  6860. collectDtsInfo = function(track, data) {
  6861. if (typeof data.pts === 'number') {
  6862. if (track.timelineStartInfo.pts === undefined) {
  6863. track.timelineStartInfo.pts = data.pts;
  6864. }
  6865. if (track.minSegmentPts === undefined) {
  6866. track.minSegmentPts = data.pts;
  6867. } else {
  6868. track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
  6869. }
  6870. if (track.maxSegmentPts === undefined) {
  6871. track.maxSegmentPts = data.pts;
  6872. } else {
  6873. track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
  6874. }
  6875. }
  6876. if (typeof data.dts === 'number') {
  6877. if (track.timelineStartInfo.dts === undefined) {
  6878. track.timelineStartInfo.dts = data.dts;
  6879. }
  6880. if (track.minSegmentDts === undefined) {
  6881. track.minSegmentDts = data.dts;
  6882. } else {
  6883. track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
  6884. }
  6885. if (track.maxSegmentDts === undefined) {
  6886. track.maxSegmentDts = data.dts;
  6887. } else {
  6888. track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
  6889. }
  6890. }
  6891. };
  6892. /**
  6893. * Clear values used to calculate the baseMediaDecodeTime between
  6894. * tracks
  6895. */
  6896. clearDtsInfo = function(track) {
  6897. delete track.minSegmentDts;
  6898. delete track.maxSegmentDts;
  6899. delete track.minSegmentPts;
  6900. delete track.maxSegmentPts;
  6901. };
  6902. /**
  6903. * Calculate the track's baseMediaDecodeTime based on the earliest
  6904. * DTS the transmuxer has ever seen and the minimum DTS for the
  6905. * current track
  6906. */
  6907. calculateTrackBaseMediaDecodeTime = function(track) {
  6908. var
  6909. baseMediaDecodeTime,
  6910. scale,
  6911. // Calculate the distance, in time, that this segment starts from the start
  6912. // of the timeline (earliest time seen since the transmuxer initialized)
  6913. timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
  6914. // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  6915. // we want the start of the first segment to be placed
  6916. baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
  6917. // Add to that the distance this segment is from the very first
  6918. baseMediaDecodeTime += timeSinceStartOfTimeline;
  6919. // baseMediaDecodeTime must not become negative
  6920. baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
  6921. if (track.type === 'audio') {
  6922. // Audio has a different clock equal to the sampling_rate so we need to
  6923. // scale the PTS values into the clock rate of the track
  6924. scale = track.samplerate / ONE_SECOND_IN_TS;
  6925. baseMediaDecodeTime *= scale;
  6926. baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
  6927. }
  6928. return baseMediaDecodeTime;
  6929. };
  6930. /**
  6931. * A Stream that can combine multiple streams (ie. audio & video)
  6932. * into a single output segment for MSE. Also supports audio-only
  6933. * and video-only streams.
  6934. */
  6935. CoalesceStream = function(options, metadataStream) {
  6936. // Number of Tracks per output segment
  6937. // If greater than 1, we combine multiple
  6938. // tracks into a single segment
  6939. this.numberOfTracks = 0;
  6940. this.metadataStream = metadataStream;
  6941. if (typeof options.remux !== 'undefined') {
  6942. this.remuxTracks = !!options.remux;
  6943. } else {
  6944. this.remuxTracks = true;
  6945. }
  6946. this.pendingTracks = [];
  6947. this.videoTrack = null;
  6948. this.pendingBoxes = [];
  6949. this.pendingCaptions = [];
  6950. this.pendingMetadata = [];
  6951. this.pendingBytes = 0;
  6952. this.emittedTracks = 0;
  6953. CoalesceStream.prototype.init.call(this);
  6954. // Take output from multiple
  6955. this.push = function(output) {
  6956. // buffer incoming captions until the associated video segment
  6957. // finishes
  6958. if (output.text) {
  6959. return this.pendingCaptions.push(output);
  6960. }
  6961. // buffer incoming id3 tags until the final flush
  6962. if (output.frames) {
  6963. return this.pendingMetadata.push(output);
  6964. }
  6965. // Add this track to the list of pending tracks and store
  6966. // important information required for the construction of
  6967. // the final segment
  6968. this.pendingTracks.push(output.track);
  6969. this.pendingBoxes.push(output.boxes);
  6970. this.pendingBytes += output.boxes.byteLength;
  6971. if (output.track.type === 'video') {
  6972. this.videoTrack = output.track;
  6973. }
  6974. if (output.track.type === 'audio') {
  6975. this.audioTrack = output.track;
  6976. }
  6977. };
  6978. };
  6979. CoalesceStream.prototype = new Stream();
  6980. CoalesceStream.prototype.flush = function(flushSource) {
  6981. var
  6982. offset = 0,
  6983. event = {
  6984. captions: [],
  6985. captionStreams: {},
  6986. metadata: [],
  6987. info: {}
  6988. },
  6989. caption,
  6990. id3,
  6991. initSegment,
  6992. timelineStartPts = 0,
  6993. i;
  6994. if (this.pendingTracks.length < this.numberOfTracks) {
  6995. if (flushSource !== 'VideoSegmentStream' &&
  6996. flushSource !== 'AudioSegmentStream') {
  6997. // Return because we haven't received a flush from a data-generating
  6998. // portion of the segment (meaning that we have only recieved meta-data
  6999. // or captions.)
  7000. return;
  7001. } else if (this.remuxTracks) {
  7002. // Return until we have enough tracks from the pipeline to remux (if we
  7003. // are remuxing audio and video into a single MP4)
  7004. return;
  7005. } else if (this.pendingTracks.length === 0) {
  7006. // In the case where we receive a flush without any data having been
  7007. // received we consider it an emitted track for the purposes of coalescing
  7008. // `done` events.
  7009. // We do this for the case where there is an audio and video track in the
  7010. // segment but no audio data. (seen in several playlists with alternate
  7011. // audio tracks and no audio present in the main TS segments.)
  7012. this.emittedTracks++;
  7013. if (this.emittedTracks >= this.numberOfTracks) {
  7014. this.trigger('done');
  7015. this.emittedTracks = 0;
  7016. }
  7017. return;
  7018. }
  7019. }
  7020. if (this.videoTrack) {
  7021. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  7022. VIDEO_PROPERTIES.forEach(function(prop) {
  7023. event.info[prop] = this.videoTrack[prop];
  7024. }, this);
  7025. } else if (this.audioTrack) {
  7026. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  7027. AUDIO_PROPERTIES.forEach(function(prop) {
  7028. event.info[prop] = this.audioTrack[prop];
  7029. }, this);
  7030. }
  7031. if (this.pendingTracks.length === 1) {
  7032. event.type = this.pendingTracks[0].type;
  7033. } else {
  7034. event.type = 'combined';
  7035. }
  7036. this.emittedTracks += this.pendingTracks.length;
  7037. initSegment = mp4.initSegment(this.pendingTracks);
  7038. // Create a new typed array to hold the init segment
  7039. event.initSegment = new Uint8Array(initSegment.byteLength);
  7040. // Create an init segment containing a moov
  7041. // and track definitions
  7042. event.initSegment.set(initSegment);
  7043. // Create a new typed array to hold the moof+mdats
  7044. event.data = new Uint8Array(this.pendingBytes);
  7045. // Append each moof+mdat (one per track) together
  7046. for (i = 0; i < this.pendingBoxes.length; i++) {
  7047. event.data.set(this.pendingBoxes[i], offset);
  7048. offset += this.pendingBoxes[i].byteLength;
  7049. }
  7050. // Translate caption PTS times into second offsets into the
  7051. // video timeline for the segment, and add track info
  7052. for (i = 0; i < this.pendingCaptions.length; i++) {
  7053. caption = this.pendingCaptions[i];
  7054. caption.startTime = (caption.startPts - timelineStartPts);
  7055. caption.startTime /= 90e3;
  7056. caption.endTime = (caption.endPts - timelineStartPts);
  7057. caption.endTime /= 90e3;
  7058. event.captionStreams[caption.stream] = true;
  7059. event.captions.push(caption);
  7060. }
  7061. // Translate ID3 frame PTS times into second offsets into the
  7062. // video timeline for the segment
  7063. for (i = 0; i < this.pendingMetadata.length; i++) {
  7064. id3 = this.pendingMetadata[i];
  7065. id3.cueTime = (id3.pts - timelineStartPts);
  7066. id3.cueTime /= 90e3;
  7067. event.metadata.push(id3);
  7068. }
  7069. // We add this to every single emitted segment even though we only need
  7070. // it for the first
  7071. event.metadata.dispatchType = this.metadataStream.dispatchType;
  7072. // Reset stream state
  7073. this.pendingTracks.length = 0;
  7074. this.videoTrack = null;
  7075. this.pendingBoxes.length = 0;
  7076. this.pendingCaptions.length = 0;
  7077. this.pendingBytes = 0;
  7078. this.pendingMetadata.length = 0;
  7079. // Emit the built segment
  7080. this.trigger('data', event);
  7081. // Only emit `done` if all tracks have been flushed and emitted
  7082. if (this.emittedTracks >= this.numberOfTracks) {
  7083. this.trigger('done');
  7084. this.emittedTracks = 0;
  7085. }
  7086. };
  7087. /**
  7088. * A Stream that expects MP2T binary data as input and produces
  7089. * corresponding media segments, suitable for use with Media Source
  7090. * Extension (MSE) implementations that support the ISO BMFF byte
  7091. * stream format, like Chrome.
  7092. */
  7093. Transmuxer = function(options) {
  7094. var
  7095. self = this,
  7096. hasFlushed = true,
  7097. videoTrack,
  7098. audioTrack;
  7099. Transmuxer.prototype.init.call(this);
  7100. options = options || {};
  7101. this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  7102. this.transmuxPipeline_ = {};
  7103. this.setupAacPipeline = function() {
  7104. var pipeline = {};
  7105. this.transmuxPipeline_ = pipeline;
  7106. pipeline.type = 'aac';
  7107. pipeline.metadataStream = new m2ts.MetadataStream();
  7108. // set up the parsing pipeline
  7109. pipeline.aacStream = new AacStream();
  7110. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  7111. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  7112. pipeline.adtsStream = new AdtsStream();
  7113. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  7114. pipeline.headOfPipeline = pipeline.aacStream;
  7115. pipeline.aacStream
  7116. .pipe(pipeline.audioTimestampRolloverStream)
  7117. .pipe(pipeline.adtsStream);
  7118. pipeline.aacStream
  7119. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  7120. .pipe(pipeline.metadataStream)
  7121. .pipe(pipeline.coalesceStream);
  7122. pipeline.metadataStream.on('timestamp', function(frame) {
  7123. pipeline.aacStream.setTimestamp(frame.timeStamp);
  7124. });
  7125. pipeline.aacStream.on('data', function(data) {
  7126. if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
  7127. audioTrack = audioTrack || {
  7128. timelineStartInfo: {
  7129. baseMediaDecodeTime: self.baseMediaDecodeTime
  7130. },
  7131. codec: 'adts',
  7132. type: 'audio'
  7133. };
  7134. // hook up the audio segment stream to the first track with aac data
  7135. pipeline.coalesceStream.numberOfTracks++;
  7136. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  7137. // Set up the final part of the audio pipeline
  7138. pipeline.adtsStream
  7139. .pipe(pipeline.audioSegmentStream)
  7140. .pipe(pipeline.coalesceStream);
  7141. }
  7142. });
  7143. // Re-emit any data coming from the coalesce stream to the outside world
  7144. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  7145. // Let the consumer know we have finished flushing the entire pipeline
  7146. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  7147. };
  7148. this.setupTsPipeline = function() {
  7149. var pipeline = {};
  7150. this.transmuxPipeline_ = pipeline;
  7151. pipeline.type = 'ts';
  7152. pipeline.metadataStream = new m2ts.MetadataStream();
  7153. // set up the parsing pipeline
  7154. pipeline.packetStream = new m2ts.TransportPacketStream();
  7155. pipeline.parseStream = new m2ts.TransportParseStream();
  7156. pipeline.elementaryStream = new m2ts.ElementaryStream();
  7157. pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  7158. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  7159. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  7160. pipeline.adtsStream = new AdtsStream();
  7161. pipeline.h264Stream = new H264Stream();
  7162. pipeline.captionStream = new m2ts.CaptionStream();
  7163. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  7164. pipeline.headOfPipeline = pipeline.packetStream;
  7165. // disassemble MPEG2-TS packets into elementary streams
  7166. pipeline.packetStream
  7167. .pipe(pipeline.parseStream)
  7168. .pipe(pipeline.elementaryStream);
  7169. // !!THIS ORDER IS IMPORTANT!!
  7170. // demux the streams
  7171. pipeline.elementaryStream
  7172. .pipe(pipeline.videoTimestampRolloverStream)
  7173. .pipe(pipeline.h264Stream);
  7174. pipeline.elementaryStream
  7175. .pipe(pipeline.audioTimestampRolloverStream)
  7176. .pipe(pipeline.adtsStream);
  7177. pipeline.elementaryStream
  7178. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  7179. .pipe(pipeline.metadataStream)
  7180. .pipe(pipeline.coalesceStream);
  7181. // Hook up CEA-608/708 caption stream
  7182. pipeline.h264Stream.pipe(pipeline.captionStream)
  7183. .pipe(pipeline.coalesceStream);
  7184. pipeline.elementaryStream.on('data', function(data) {
  7185. var i;
  7186. if (data.type === 'metadata') {
  7187. i = data.tracks.length;
  7188. // scan the tracks listed in the metadata
  7189. while (i--) {
  7190. if (!videoTrack && data.tracks[i].type === 'video') {
  7191. videoTrack = data.tracks[i];
  7192. videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  7193. } else if (!audioTrack && data.tracks[i].type === 'audio') {
  7194. audioTrack = data.tracks[i];
  7195. audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  7196. }
  7197. }
  7198. // hook up the video segment stream to the first track with h264 data
  7199. if (videoTrack && !pipeline.videoSegmentStream) {
  7200. pipeline.coalesceStream.numberOfTracks++;
  7201. pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack, options);
  7202. pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
  7203. // When video emits timelineStartInfo data after a flush, we forward that
  7204. // info to the AudioSegmentStream, if it exists, because video timeline
  7205. // data takes precedence.
  7206. if (audioTrack) {
  7207. audioTrack.timelineStartInfo = timelineStartInfo;
  7208. // On the first segment we trim AAC frames that exist before the
  7209. // very earliest DTS we have seen in video because Chrome will
  7210. // interpret any video track with a baseMediaDecodeTime that is
  7211. // non-zero as a gap.
  7212. pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
  7213. }
  7214. });
  7215. pipeline.videoSegmentStream.on('processedGopsInfo',
  7216. self.trigger.bind(self, 'gopInfo'));
  7217. pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
  7218. if (audioTrack) {
  7219. pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
  7220. }
  7221. });
  7222. // Set up the final part of the video pipeline
  7223. pipeline.h264Stream
  7224. .pipe(pipeline.videoSegmentStream)
  7225. .pipe(pipeline.coalesceStream);
  7226. }
  7227. if (audioTrack && !pipeline.audioSegmentStream) {
  7228. // hook up the audio segment stream to the first track with aac data
  7229. pipeline.coalesceStream.numberOfTracks++;
  7230. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  7231. // Set up the final part of the audio pipeline
  7232. pipeline.adtsStream
  7233. .pipe(pipeline.audioSegmentStream)
  7234. .pipe(pipeline.coalesceStream);
  7235. }
  7236. }
  7237. });
  7238. // Re-emit any data coming from the coalesce stream to the outside world
  7239. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  7240. // Let the consumer know we have finished flushing the entire pipeline
  7241. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  7242. };
  7243. // hook up the segment streams once track metadata is delivered
  7244. this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  7245. var pipeline = this.transmuxPipeline_;
  7246. this.baseMediaDecodeTime = baseMediaDecodeTime;
  7247. if (audioTrack) {
  7248. audioTrack.timelineStartInfo.dts = undefined;
  7249. audioTrack.timelineStartInfo.pts = undefined;
  7250. clearDtsInfo(audioTrack);
  7251. audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  7252. if (pipeline.audioTimestampRolloverStream) {
  7253. pipeline.audioTimestampRolloverStream.discontinuity();
  7254. }
  7255. }
  7256. if (videoTrack) {
  7257. if (pipeline.videoSegmentStream) {
  7258. pipeline.videoSegmentStream.gopCache_ = [];
  7259. pipeline.videoTimestampRolloverStream.discontinuity();
  7260. }
  7261. videoTrack.timelineStartInfo.dts = undefined;
  7262. videoTrack.timelineStartInfo.pts = undefined;
  7263. clearDtsInfo(videoTrack);
  7264. pipeline.captionStream.reset();
  7265. videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  7266. }
  7267. if (pipeline.timedMetadataTimestampRolloverStream) {
  7268. pipeline.timedMetadataTimestampRolloverStream.discontinuity();
  7269. }
  7270. };
  7271. this.setAudioAppendStart = function(timestamp) {
  7272. if (audioTrack) {
  7273. this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
  7274. }
  7275. };
  7276. this.alignGopsWith = function(gopsToAlignWith) {
  7277. if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
  7278. this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
  7279. }
  7280. };
  7281. // feed incoming data to the front of the parsing pipeline
  7282. this.push = function(data) {
  7283. if (hasFlushed) {
  7284. var isAac = isLikelyAacData(data);
  7285. if (isAac && this.transmuxPipeline_.type !== 'aac') {
  7286. this.setupAacPipeline();
  7287. } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
  7288. this.setupTsPipeline();
  7289. }
  7290. hasFlushed = false;
  7291. }
  7292. this.transmuxPipeline_.headOfPipeline.push(data);
  7293. };
  7294. // flush any buffered data
  7295. this.flush = function() {
  7296. hasFlushed = true;
  7297. // Start at the top of the pipeline and flush all pending work
  7298. this.transmuxPipeline_.headOfPipeline.flush();
  7299. };
  7300. // Caption data has to be reset when seeking outside buffered range
  7301. this.resetCaptions = function() {
  7302. if (this.transmuxPipeline_.captionStream) {
  7303. this.transmuxPipeline_.captionStream.reset();
  7304. }
  7305. };
  7306. };
  7307. Transmuxer.prototype = new Stream();
  7308. module.exports = {
  7309. Transmuxer: Transmuxer,
  7310. VideoSegmentStream: VideoSegmentStream,
  7311. AudioSegmentStream: AudioSegmentStream,
  7312. AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  7313. VIDEO_PROPERTIES: VIDEO_PROPERTIES
  7314. };
  7315. },{"../aac":15,"../codecs/adts.js":16,"../codecs/h264":17,"../data/silence":18,"../m2ts/m2ts.js":26,"../utils/clock":33,"../utils/stream.js":35,"./mp4-generator.js":31}],33:[function(require,module,exports){
  7316. var
  7317. ONE_SECOND_IN_TS = 90000, // 90kHz clock
  7318. secondsToVideoTs,
  7319. secondsToAudioTs,
  7320. videoTsToSeconds,
  7321. audioTsToSeconds,
  7322. audioTsToVideoTs,
  7323. videoTsToAudioTs;
  7324. secondsToVideoTs = function(seconds) {
  7325. return seconds * ONE_SECOND_IN_TS;
  7326. };
  7327. secondsToAudioTs = function(seconds, sampleRate) {
  7328. return seconds * sampleRate;
  7329. };
  7330. videoTsToSeconds = function(timestamp) {
  7331. return timestamp / ONE_SECOND_IN_TS;
  7332. };
  7333. audioTsToSeconds = function(timestamp, sampleRate) {
  7334. return timestamp / sampleRate;
  7335. };
  7336. audioTsToVideoTs = function(timestamp, sampleRate) {
  7337. return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
  7338. };
  7339. videoTsToAudioTs = function(timestamp, sampleRate) {
  7340. return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
  7341. };
  7342. module.exports = {
  7343. secondsToVideoTs: secondsToVideoTs,
  7344. secondsToAudioTs: secondsToAudioTs,
  7345. videoTsToSeconds: videoTsToSeconds,
  7346. audioTsToSeconds: audioTsToSeconds,
  7347. audioTsToVideoTs: audioTsToVideoTs,
  7348. videoTsToAudioTs: videoTsToAudioTs
  7349. };
  7350. },{}],34:[function(require,module,exports){
  7351. 'use strict';
  7352. var ExpGolomb;
  7353. /**
  7354. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  7355. * scheme used by h264.
  7356. */
  7357. ExpGolomb = function(workingData) {
  7358. var
  7359. // the number of bytes left to examine in workingData
  7360. workingBytesAvailable = workingData.byteLength,
  7361. // the current word being examined
  7362. workingWord = 0, // :uint
  7363. // the number of bits left to examine in the current word
  7364. workingBitsAvailable = 0; // :uint;
  7365. // ():uint
  7366. this.length = function() {
  7367. return (8 * workingBytesAvailable);
  7368. };
  7369. // ():uint
  7370. this.bitsAvailable = function() {
  7371. return (8 * workingBytesAvailable) + workingBitsAvailable;
  7372. };
  7373. // ():void
  7374. this.loadWord = function() {
  7375. var
  7376. position = workingData.byteLength - workingBytesAvailable,
  7377. workingBytes = new Uint8Array(4),
  7378. availableBytes = Math.min(4, workingBytesAvailable);
  7379. if (availableBytes === 0) {
  7380. throw new Error('no bytes available');
  7381. }
  7382. workingBytes.set(workingData.subarray(position,
  7383. position + availableBytes));
  7384. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  7385. // track the amount of workingData that has been processed
  7386. workingBitsAvailable = availableBytes * 8;
  7387. workingBytesAvailable -= availableBytes;
  7388. };
  7389. // (count:int):void
  7390. this.skipBits = function(count) {
  7391. var skipBytes; // :int
  7392. if (workingBitsAvailable > count) {
  7393. workingWord <<= count;
  7394. workingBitsAvailable -= count;
  7395. } else {
  7396. count -= workingBitsAvailable;
  7397. skipBytes = Math.floor(count / 8);
  7398. count -= (skipBytes * 8);
  7399. workingBytesAvailable -= skipBytes;
  7400. this.loadWord();
  7401. workingWord <<= count;
  7402. workingBitsAvailable -= count;
  7403. }
  7404. };
  7405. // (size:int):uint
  7406. this.readBits = function(size) {
  7407. var
  7408. bits = Math.min(workingBitsAvailable, size), // :uint
  7409. valu = workingWord >>> (32 - bits); // :uint
  7410. // if size > 31, handle error
  7411. workingBitsAvailable -= bits;
  7412. if (workingBitsAvailable > 0) {
  7413. workingWord <<= bits;
  7414. } else if (workingBytesAvailable > 0) {
  7415. this.loadWord();
  7416. }
  7417. bits = size - bits;
  7418. if (bits > 0) {
  7419. return valu << bits | this.readBits(bits);
  7420. }
  7421. return valu;
  7422. };
  7423. // ():uint
  7424. this.skipLeadingZeros = function() {
  7425. var leadingZeroCount; // :uint
  7426. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  7427. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  7428. // the first bit of working word is 1
  7429. workingWord <<= leadingZeroCount;
  7430. workingBitsAvailable -= leadingZeroCount;
  7431. return leadingZeroCount;
  7432. }
  7433. }
  7434. // we exhausted workingWord and still have not found a 1
  7435. this.loadWord();
  7436. return leadingZeroCount + this.skipLeadingZeros();
  7437. };
  7438. // ():void
  7439. this.skipUnsignedExpGolomb = function() {
  7440. this.skipBits(1 + this.skipLeadingZeros());
  7441. };
  7442. // ():void
  7443. this.skipExpGolomb = function() {
  7444. this.skipBits(1 + this.skipLeadingZeros());
  7445. };
  7446. // ():uint
  7447. this.readUnsignedExpGolomb = function() {
  7448. var clz = this.skipLeadingZeros(); // :uint
  7449. return this.readBits(clz + 1) - 1;
  7450. };
  7451. // ():int
  7452. this.readExpGolomb = function() {
  7453. var valu = this.readUnsignedExpGolomb(); // :int
  7454. if (0x01 & valu) {
  7455. // the number is odd if the low order bit is set
  7456. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  7457. }
  7458. return -1 * (valu >>> 1); // divide by two then make it negative
  7459. };
  7460. // Some convenience functions
  7461. // :Boolean
  7462. this.readBoolean = function() {
  7463. return this.readBits(1) === 1;
  7464. };
  7465. // ():int
  7466. this.readUnsignedByte = function() {
  7467. return this.readBits(8);
  7468. };
  7469. this.loadWord();
  7470. };
  7471. module.exports = ExpGolomb;
  7472. },{}],35:[function(require,module,exports){
  7473. /**
  7474. * mux.js
  7475. *
  7476. * Copyright (c) 2014 Brightcove
  7477. * All rights reserved.
  7478. *
  7479. * A lightweight readable stream implemention that handles event dispatching.
  7480. * Objects that inherit from streams should call init in their constructors.
  7481. */
  7482. 'use strict';
  7483. var Stream = function() {
  7484. this.init = function() {
  7485. var listeners = {};
  7486. /**
  7487. * Add a listener for a specified event type.
  7488. * @param type {string} the event name
  7489. * @param listener {function} the callback to be invoked when an event of
  7490. * the specified type occurs
  7491. */
  7492. this.on = function(type, listener) {
  7493. if (!listeners[type]) {
  7494. listeners[type] = [];
  7495. }
  7496. listeners[type] = listeners[type].concat(listener);
  7497. };
  7498. /**
  7499. * Remove a listener for a specified event type.
  7500. * @param type {string} the event name
  7501. * @param listener {function} a function previously registered for this
  7502. * type of event through `on`
  7503. */
  7504. this.off = function(type, listener) {
  7505. var index;
  7506. if (!listeners[type]) {
  7507. return false;
  7508. }
  7509. index = listeners[type].indexOf(listener);
  7510. listeners[type] = listeners[type].slice();
  7511. listeners[type].splice(index, 1);
  7512. return index > -1;
  7513. };
  7514. /**
  7515. * Trigger an event of the specified type on this stream. Any additional
  7516. * arguments to this function are passed as parameters to event listeners.
  7517. * @param type {string} the event name
  7518. */
  7519. this.trigger = function(type) {
  7520. var callbacks, i, length, args;
  7521. callbacks = listeners[type];
  7522. if (!callbacks) {
  7523. return;
  7524. }
  7525. // Slicing the arguments on every invocation of this method
  7526. // can add a significant amount of overhead. Avoid the
  7527. // intermediate object creation for the common case of a
  7528. // single callback argument
  7529. if (arguments.length === 2) {
  7530. length = callbacks.length;
  7531. for (i = 0; i < length; ++i) {
  7532. callbacks[i].call(this, arguments[1]);
  7533. }
  7534. } else {
  7535. args = [];
  7536. i = arguments.length;
  7537. for (i = 1; i < arguments.length; ++i) {
  7538. args.push(arguments[i]);
  7539. }
  7540. length = callbacks.length;
  7541. for (i = 0; i < length; ++i) {
  7542. callbacks[i].apply(this, args);
  7543. }
  7544. }
  7545. };
  7546. /**
  7547. * Destroys the stream and cleans up.
  7548. */
  7549. this.dispose = function() {
  7550. listeners = {};
  7551. };
  7552. };
  7553. };
  7554. /**
  7555. * Forwards all `data` events on this stream to the destination stream. The
  7556. * destination stream should provide a method `push` to receive the data
  7557. * events as they arrive.
  7558. * @param destination {stream} the stream that will receive all `data` events
  7559. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  7560. * when the current stream emits a 'done' event
  7561. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  7562. */
  7563. Stream.prototype.pipe = function(destination) {
  7564. this.on('data', function(data) {
  7565. destination.push(data);
  7566. });
  7567. this.on('done', function(flushSource) {
  7568. destination.flush(flushSource);
  7569. });
  7570. return destination;
  7571. };
  7572. // Default stream functions that are expected to be overridden to perform
  7573. // actual work. These are provided by the prototype as a sort of no-op
  7574. // implementation so that we don't have to check for their existence in the
  7575. // `pipe` function above.
  7576. Stream.prototype.push = function(data) {
  7577. this.trigger('data', data);
  7578. };
  7579. Stream.prototype.flush = function(flushSource) {
  7580. this.trigger('done', flushSource);
  7581. };
  7582. module.exports = Stream;
  7583. },{}],36:[function(require,module,exports){
  7584. // By default assume browserify was used to bundle app. These arguments are passed to
  7585. // the module by browserify.
  7586. var bundleFn = arguments[3];
  7587. var sources = arguments[4];
  7588. var cache = arguments[5];
  7589. var stringify = JSON.stringify;
  7590. var webpack = false;
  7591. // webpackBootstrap
  7592. var webpackBootstrapFn = function(modules) {
  7593. // The module cache
  7594. var installedModules = {};
  7595. // The require function
  7596. function __webpack_require__(moduleId) {
  7597. // Check if module is in cache
  7598. if(installedModules[moduleId]) {
  7599. return installedModules[moduleId].exports;
  7600. }
  7601. // Create a new module (and put it into the cache)
  7602. var module = installedModules[moduleId] = {
  7603. i: moduleId,
  7604. l: false,
  7605. exports: {}
  7606. };
  7607. // Execute the module function
  7608. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  7609. // Flag the module as loaded
  7610. module.l = true;
  7611. // Return the exports of the module
  7612. return module.exports;
  7613. }
  7614. // expose the modules object (__webpack_modules__)
  7615. __webpack_require__.m = modules;
  7616. // expose the module cache
  7617. __webpack_require__.c = installedModules;
  7618. // define getter function for harmony exports
  7619. __webpack_require__.d = function(exports, name, getter) {
  7620. if(!__webpack_require__.o(exports, name)) {
  7621. Object.defineProperty(exports, name, {
  7622. configurable: false,
  7623. enumerable: true,
  7624. get: getter
  7625. });
  7626. }
  7627. };
  7628. // getDefaultExport function for compatibility with non-harmony modules
  7629. __webpack_require__.n = function(module) {
  7630. var getter = module && module.__esModule ?
  7631. function getDefault() { return module['default']; } :
  7632. function getModuleExports() { return module; };
  7633. __webpack_require__.d(getter, 'a', getter);
  7634. return getter;
  7635. };
  7636. // Object.prototype.hasOwnProperty.call
  7637. __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  7638. // __webpack_public_path__
  7639. __webpack_require__.p = "";
  7640. // Load entry module and return exports
  7641. return __webpack_require__(__webpack_require__.s = entryModule);
  7642. }
  7643. if (typeof bundleFn === 'undefined') {
  7644. // Assume this was bundled with webpack and not browserify
  7645. webpack = true;
  7646. bundleFn = webpackBootstrapFn;
  7647. sources = __webpack_modules__;
  7648. }
  7649. var bundleWithBrowserify = function(fn) {
  7650. // with browserify we must find the module key ourselves
  7651. var cacheKeys = Object.keys(cache);
  7652. var fnModuleKey;
  7653. for (var i = 0; i < cacheKeys.length; i++) {
  7654. var cacheKey = cacheKeys[i];
  7655. var cacheExports = cache[cacheKey].exports;
  7656. // Using babel as a transpiler to use esmodule, the export will always
  7657. // be an object with the default export as a property of it. To ensure
  7658. // the existing api and babel esmodule exports are both supported we
  7659. // check for both
  7660. if (cacheExports === fn || cacheExports && cacheExports.default === fn) {
  7661. fnModuleKey = cacheKey;
  7662. break;
  7663. }
  7664. }
  7665. // if we couldn't find one, lets make one
  7666. if (!fnModuleKey) {
  7667. fnModuleKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  7668. var fnModuleCache = {};
  7669. for (var i = 0; i < cacheKeys.length; i++) {
  7670. var cacheKey = cacheKeys[i];
  7671. fnModuleCache[cacheKey] = cacheKey;
  7672. }
  7673. sources[fnModuleKey] = [
  7674. 'function(require,module,exports){' + fn + '(self); }',
  7675. fnModuleCache
  7676. ];
  7677. }
  7678. var entryKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  7679. var entryCache = {};
  7680. entryCache[fnModuleKey] = fnModuleKey;
  7681. sources[entryKey] = [
  7682. 'function(require,module,exports){' +
  7683. // try to call default if defined to also support babel esmodule exports
  7684. 'var f = require(' + stringify(fnModuleKey) + ');' +
  7685. '(f.default ? f.default : f)(self);' +
  7686. '}',
  7687. entryCache
  7688. ];
  7689. return '(' + bundleFn + ')({'
  7690. + Object.keys(sources).map(function(key) {
  7691. return stringify(key) + ':['
  7692. + sources[key][0] + ','
  7693. + stringify(sources[key][1]) + ']';
  7694. }).join(',')
  7695. + '},{},[' + stringify(entryKey) + '])';
  7696. };
  7697. var bundleWithWebpack = function(fn, fnModuleId) {
  7698. var devMode = typeof fnModuleId === 'string';
  7699. var sourceStrings;
  7700. if (devMode) {
  7701. sourceStrings = {};
  7702. } else {
  7703. sourceStrings = [];
  7704. }
  7705. Object.keys(sources).forEach(function(sKey) {
  7706. if (!sources[sKey]) {
  7707. return;
  7708. }
  7709. sourceStrings[sKey] = sources[sKey].toString();
  7710. });
  7711. var fnModuleExports = __webpack_require__(fnModuleId);
  7712. // Using babel as a transpiler to use esmodule, the export will always
  7713. // be an object with the default export as a property of it. To ensure
  7714. // the existing api and babel esmodule exports are both supported we
  7715. // check for both
  7716. if (!(fnModuleExports && (fnModuleExports === fn || fnModuleExports.default === fn))) {
  7717. var fnSourceString = sourceStrings[fnModuleId];
  7718. sourceStrings[fnModuleId] = fnSourceString.substring(0, fnSourceString.length - 1) +
  7719. '\n' + fn.name + '();\n}';
  7720. }
  7721. var modulesString;
  7722. if (devMode) {
  7723. // must escape quotes to support webpack loader options
  7724. fnModuleId = stringify(fnModuleId);
  7725. // dev mode in webpack4, modules are passed as an object
  7726. var mappedSourceStrings = Object.keys(sourceStrings).map(function(sKey) {
  7727. return stringify(sKey) + ':' + sourceStrings[sKey];
  7728. });
  7729. modulesString = '{' + mappedSourceStrings.join(',') + '}';
  7730. } else {
  7731. modulesString = '[' + sourceStrings.join(',') + ']';
  7732. }
  7733. return 'var fn = (' + bundleFn.toString().replace('entryModule', fnModuleId) + ')('
  7734. + modulesString
  7735. + ');\n'
  7736. // not a function when calling a function from the current scope
  7737. + '(typeof fn === "function") && fn(self);';
  7738. };
  7739. module.exports = function webwackify(fn, fnModuleId) {
  7740. var src;
  7741. if (webpack) {
  7742. src = bundleWithWebpack(fn, fnModuleId);
  7743. } else {
  7744. src = bundleWithBrowserify(fn);
  7745. }
  7746. var blob = new Blob([src], { type: 'text/javascript' });
  7747. var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  7748. var workerUrl = URL.createObjectURL(blob);
  7749. var worker = new Worker(workerUrl);
  7750. worker.objectURL = workerUrl;
  7751. return worker;
  7752. };
  7753. },{}],37:[function(require,module,exports){
  7754. (function (global){
  7755. /**
  7756. * @file videojs-contrib-media-sources.js
  7757. */
  7758. 'use strict';
  7759. Object.defineProperty(exports, '__esModule', {
  7760. value: true
  7761. });
  7762. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7763. var _globalWindow = require('global/window');
  7764. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  7765. var _flashMediaSource = require('./flash-media-source');
  7766. var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
  7767. var _htmlMediaSource = require('./html-media-source');
  7768. var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
  7769. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  7770. var _videoJs2 = _interopRequireDefault(_videoJs);
  7771. var urlCount = 0;
  7772. // ------------
  7773. // Media Source
  7774. // ------------
  7775. var defaults = {
  7776. // how to determine the MediaSource implementation to use. There
  7777. // are three available modes:
  7778. // - auto: use native MediaSources where available and Flash
  7779. // everywhere else
  7780. // - html5: always use native MediaSources
  7781. // - flash: always use the Flash MediaSource polyfill
  7782. mode: 'auto'
  7783. };
  7784. // store references to the media sources so they can be connected
  7785. // to a video element (a swf object)
  7786. // TODO: can we store this somewhere local to this module?
  7787. _videoJs2['default'].mediaSources = {};
  7788. /**
  7789. * Provide a method for a swf object to notify JS that a
  7790. * media source is now open.
  7791. *
  7792. * @param {String} msObjectURL string referencing the MSE Object URL
  7793. * @param {String} swfId the swf id
  7794. */
  7795. var open = function open(msObjectURL, swfId) {
  7796. var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
  7797. if (mediaSource) {
  7798. mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
  7799. } else {
  7800. throw new Error('Media Source not found (Video.js)');
  7801. }
  7802. };
  7803. /**
  7804. * Check to see if the native MediaSource object exists and supports
  7805. * an MP4 container with both H.264 video and AAC-LC audio.
  7806. *
  7807. * @return {Boolean} if native media sources are supported
  7808. */
  7809. var supportsNativeMediaSources = function supportsNativeMediaSources() {
  7810. return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
  7811. };
  7812. /**
  7813. * An emulation of the MediaSource API so that we can support
  7814. * native and non-native functionality such as flash and
  7815. * video/mp2t videos. returns an instance of HtmlMediaSource or
  7816. * FlashMediaSource depending on what is supported and what options
  7817. * are passed in.
  7818. *
  7819. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
  7820. * @param {Object} options options to use during setup.
  7821. */
  7822. var MediaSource = function MediaSource(options) {
  7823. var settings = _videoJs2['default'].mergeOptions(defaults, options);
  7824. this.MediaSource = {
  7825. open: open,
  7826. supportsNativeMediaSources: supportsNativeMediaSources
  7827. };
  7828. // determine whether HTML MediaSources should be used
  7829. if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
  7830. return new _htmlMediaSource2['default']();
  7831. } else if (_videoJs2['default'].getTech('Flash')) {
  7832. return new _flashMediaSource2['default']();
  7833. }
  7834. throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
  7835. };
  7836. exports.MediaSource = MediaSource;
  7837. MediaSource.open = open;
  7838. MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
  7839. /**
  7840. * A wrapper around the native URL for our MSE object
  7841. * implementation, this object is exposed under videojs.URL
  7842. *
  7843. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
  7844. */
  7845. var URL = {
  7846. /**
  7847. * A wrapper around the native createObjectURL for our objects.
  7848. * This function maps a native or emulated mediaSource to a blob
  7849. * url so that it can be loaded into video.js
  7850. *
  7851. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
  7852. * @param {MediaSource} object the object to create a blob url to
  7853. */
  7854. createObjectURL: function createObjectURL(object) {
  7855. var objectUrlPrefix = 'blob:vjs-media-source/';
  7856. var url = undefined;
  7857. // use the native MediaSource to generate an object URL
  7858. if (object instanceof _htmlMediaSource2['default']) {
  7859. url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
  7860. object.url_ = url;
  7861. return url;
  7862. }
  7863. // if the object isn't an emulated MediaSource, delegate to the
  7864. // native implementation
  7865. if (!(object instanceof _flashMediaSource2['default'])) {
  7866. url = _globalWindow2['default'].URL.createObjectURL(object);
  7867. object.url_ = url;
  7868. return url;
  7869. }
  7870. // build a URL that can be used to map back to the emulated
  7871. // MediaSource
  7872. url = objectUrlPrefix + urlCount;
  7873. urlCount++;
  7874. // setup the mapping back to object
  7875. _videoJs2['default'].mediaSources[url] = object;
  7876. return url;
  7877. }
  7878. };
  7879. exports.URL = URL;
  7880. _videoJs2['default'].MediaSource = MediaSource;
  7881. _videoJs2['default'].URL = URL;
  7882. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  7883. },{"./flash-media-source":5,"./html-media-source":8,"global/window":14}]},{},[37])(37)
  7884. });