mux-mp4.js 149 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996
  1. (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.muxjs = 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){
  2. /**
  3. * mux.js
  4. *
  5. * Copyright (c) 2016 Brightcove
  6. * All rights reserved.
  7. *
  8. * A stream-based aac to mp4 converter. This utility can be used to
  9. * deliver mp4s to a SourceBuffer on platforms that support native
  10. * Media Source Extensions.
  11. */
  12. 'use strict';
  13. var Stream = require('../utils/stream.js');
  14. // Constants
  15. var AacStream;
  16. /**
  17. * Splits an incoming stream of binary data into ADTS and ID3 Frames.
  18. */
  19. AacStream = function() {
  20. var
  21. everything = new Uint8Array(),
  22. timeStamp = 0;
  23. AacStream.prototype.init.call(this);
  24. this.setTimestamp = function(timestamp) {
  25. timeStamp = timestamp;
  26. };
  27. this.parseId3TagSize = function(header, byteIndex) {
  28. var
  29. returnSize = (header[byteIndex + 6] << 21) |
  30. (header[byteIndex + 7] << 14) |
  31. (header[byteIndex + 8] << 7) |
  32. (header[byteIndex + 9]),
  33. flags = header[byteIndex + 5],
  34. footerPresent = (flags & 16) >> 4;
  35. if (footerPresent) {
  36. return returnSize + 20;
  37. }
  38. return returnSize + 10;
  39. };
  40. this.parseAdtsSize = function(header, byteIndex) {
  41. var
  42. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  43. middle = header[byteIndex + 4] << 3,
  44. highTwo = header[byteIndex + 3] & 0x3 << 11;
  45. return (highTwo | middle) | lowThree;
  46. };
  47. this.push = function(bytes) {
  48. var
  49. frameSize = 0,
  50. byteIndex = 0,
  51. bytesLeft,
  52. chunk,
  53. packet,
  54. tempLength;
  55. // If there are bytes remaining from the last segment, prepend them to the
  56. // bytes that were pushed in
  57. if (everything.length) {
  58. tempLength = everything.length;
  59. everything = new Uint8Array(bytes.byteLength + tempLength);
  60. everything.set(everything.subarray(0, tempLength));
  61. everything.set(bytes, tempLength);
  62. } else {
  63. everything = bytes;
  64. }
  65. while (everything.length - byteIndex >= 3) {
  66. if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
  67. (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  68. (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
  69. // Exit early because we don't have enough to parse
  70. // the ID3 tag header
  71. if (everything.length - byteIndex < 10) {
  72. break;
  73. }
  74. // check framesize
  75. frameSize = this.parseId3TagSize(everything, byteIndex);
  76. // Exit early if we don't have enough in the buffer
  77. // to emit a full packet
  78. if (frameSize > everything.length) {
  79. break;
  80. }
  81. chunk = {
  82. type: 'timed-metadata',
  83. data: everything.subarray(byteIndex, byteIndex + frameSize)
  84. };
  85. this.trigger('data', chunk);
  86. byteIndex += frameSize;
  87. continue;
  88. } else if ((everything[byteIndex] & 0xff === 0xff) &&
  89. ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
  90. // Exit early because we don't have enough to parse
  91. // the ADTS frame header
  92. if (everything.length - byteIndex < 7) {
  93. break;
  94. }
  95. frameSize = this.parseAdtsSize(everything, byteIndex);
  96. // Exit early if we don't have enough in the buffer
  97. // to emit a full packet
  98. if (frameSize > everything.length) {
  99. break;
  100. }
  101. packet = {
  102. type: 'audio',
  103. data: everything.subarray(byteIndex, byteIndex + frameSize),
  104. pts: timeStamp,
  105. dts: timeStamp
  106. };
  107. this.trigger('data', packet);
  108. byteIndex += frameSize;
  109. continue;
  110. }
  111. byteIndex++;
  112. }
  113. bytesLeft = everything.length - byteIndex;
  114. if (bytesLeft > 0) {
  115. everything = everything.subarray(byteIndex);
  116. } else {
  117. everything = new Uint8Array();
  118. }
  119. };
  120. };
  121. AacStream.prototype = new Stream();
  122. module.exports = AacStream;
  123. },{"../utils/stream.js":15}],2:[function(require,module,exports){
  124. 'use strict';
  125. var Stream = require('../utils/stream.js');
  126. var AdtsStream;
  127. var
  128. ADTS_SAMPLING_FREQUENCIES = [
  129. 96000,
  130. 88200,
  131. 64000,
  132. 48000,
  133. 44100,
  134. 32000,
  135. 24000,
  136. 22050,
  137. 16000,
  138. 12000,
  139. 11025,
  140. 8000,
  141. 7350
  142. ];
  143. /*
  144. * Accepts a ElementaryStream and emits data events with parsed
  145. * AAC Audio Frames of the individual packets. Input audio in ADTS
  146. * format is unpacked and re-emitted as AAC frames.
  147. *
  148. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  149. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  150. */
  151. AdtsStream = function() {
  152. var buffer;
  153. AdtsStream.prototype.init.call(this);
  154. this.push = function(packet) {
  155. var
  156. i = 0,
  157. frameNum = 0,
  158. frameLength,
  159. protectionSkipBytes,
  160. frameEnd,
  161. oldBuffer,
  162. sampleCount,
  163. adtsFrameDuration;
  164. if (packet.type !== 'audio') {
  165. // ignore non-audio data
  166. return;
  167. }
  168. // Prepend any data in the buffer to the input data so that we can parse
  169. // aac frames the cross a PES packet boundary
  170. if (buffer) {
  171. oldBuffer = buffer;
  172. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  173. buffer.set(oldBuffer);
  174. buffer.set(packet.data, oldBuffer.byteLength);
  175. } else {
  176. buffer = packet.data;
  177. }
  178. // unpack any ADTS frames which have been fully received
  179. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  180. while (i + 5 < buffer.length) {
  181. // Loook for the start of an ADTS header..
  182. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  183. // If a valid header was not found, jump one forward and attempt to
  184. // find a valid ADTS header starting at the next byte
  185. i++;
  186. continue;
  187. }
  188. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  189. // end of the ADTS header
  190. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  191. // Frame length is a 13 bit integer starting 16 bits from the
  192. // end of the sync sequence
  193. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  194. (buffer[i + 4] << 3) |
  195. ((buffer[i + 5] & 0xe0) >> 5);
  196. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  197. adtsFrameDuration = (sampleCount * 90000) /
  198. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  199. frameEnd = i + frameLength;
  200. // If we don't have enough data to actually finish this ADTS frame, return
  201. // and wait for more data
  202. if (buffer.byteLength < frameEnd) {
  203. return;
  204. }
  205. // Otherwise, deliver the complete AAC frame
  206. this.trigger('data', {
  207. pts: packet.pts + (frameNum * adtsFrameDuration),
  208. dts: packet.dts + (frameNum * adtsFrameDuration),
  209. sampleCount: sampleCount,
  210. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  211. channelcount: ((buffer[i + 2] & 1) << 2) |
  212. ((buffer[i + 3] & 0xc0) >>> 6),
  213. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  214. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  215. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  216. samplesize: 16,
  217. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  218. });
  219. // If the buffer is empty, clear it and return
  220. if (buffer.byteLength === frameEnd) {
  221. buffer = undefined;
  222. return;
  223. }
  224. frameNum++;
  225. // Remove the finished frame from the buffer and start the process again
  226. buffer = buffer.subarray(frameEnd);
  227. }
  228. };
  229. this.flush = function() {
  230. this.trigger('done');
  231. };
  232. };
  233. AdtsStream.prototype = new Stream();
  234. module.exports = AdtsStream;
  235. },{"../utils/stream.js":15}],3:[function(require,module,exports){
  236. 'use strict';
  237. var Stream = require('../utils/stream.js');
  238. var ExpGolomb = require('../utils/exp-golomb.js');
  239. var H264Stream, NalByteStream;
  240. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  241. /**
  242. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  243. */
  244. NalByteStream = function() {
  245. var
  246. syncPoint = 0,
  247. i,
  248. buffer;
  249. NalByteStream.prototype.init.call(this);
  250. this.push = function(data) {
  251. var swapBuffer;
  252. if (!buffer) {
  253. buffer = data.data;
  254. } else {
  255. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  256. swapBuffer.set(buffer);
  257. swapBuffer.set(data.data, buffer.byteLength);
  258. buffer = swapBuffer;
  259. }
  260. // Rec. ITU-T H.264, Annex B
  261. // scan for NAL unit boundaries
  262. // a match looks like this:
  263. // 0 0 1 .. NAL .. 0 0 1
  264. // ^ sync point ^ i
  265. // or this:
  266. // 0 0 1 .. NAL .. 0 0 0
  267. // ^ sync point ^ i
  268. // advance the sync point to a NAL start, if necessary
  269. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  270. if (buffer[syncPoint + 2] === 1) {
  271. // the sync point is properly aligned
  272. i = syncPoint + 5;
  273. break;
  274. }
  275. }
  276. while (i < buffer.byteLength) {
  277. // look at the current byte to determine if we've hit the end of
  278. // a NAL unit boundary
  279. switch (buffer[i]) {
  280. case 0:
  281. // skip past non-sync sequences
  282. if (buffer[i - 1] !== 0) {
  283. i += 2;
  284. break;
  285. } else if (buffer[i - 2] !== 0) {
  286. i++;
  287. break;
  288. }
  289. // deliver the NAL unit if it isn't empty
  290. if (syncPoint + 3 !== i - 2) {
  291. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  292. }
  293. // drop trailing zeroes
  294. do {
  295. i++;
  296. } while (buffer[i] !== 1 && i < buffer.length);
  297. syncPoint = i - 2;
  298. i += 3;
  299. break;
  300. case 1:
  301. // skip past non-sync sequences
  302. if (buffer[i - 1] !== 0 ||
  303. buffer[i - 2] !== 0) {
  304. i += 3;
  305. break;
  306. }
  307. // deliver the NAL unit
  308. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  309. syncPoint = i - 2;
  310. i += 3;
  311. break;
  312. default:
  313. // the current byte isn't a one or zero, so it cannot be part
  314. // of a sync sequence
  315. i += 3;
  316. break;
  317. }
  318. }
  319. // filter out the NAL units that were delivered
  320. buffer = buffer.subarray(syncPoint);
  321. i -= syncPoint;
  322. syncPoint = 0;
  323. };
  324. this.flush = function() {
  325. // deliver the last buffered NAL unit
  326. if (buffer && buffer.byteLength > 3) {
  327. this.trigger('data', buffer.subarray(syncPoint + 3));
  328. }
  329. // reset the stream state
  330. buffer = null;
  331. syncPoint = 0;
  332. this.trigger('done');
  333. };
  334. };
  335. NalByteStream.prototype = new Stream();
  336. // values of profile_idc that indicate additional fields are included in the SPS
  337. // see Recommendation ITU-T H.264 (4/2013),
  338. // 7.3.2.1.1 Sequence parameter set data syntax
  339. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  340. 100: true,
  341. 110: true,
  342. 122: true,
  343. 244: true,
  344. 44: true,
  345. 83: true,
  346. 86: true,
  347. 118: true,
  348. 128: true,
  349. 138: true,
  350. 139: true,
  351. 134: true
  352. };
  353. /**
  354. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  355. * events.
  356. */
  357. H264Stream = function() {
  358. var
  359. nalByteStream = new NalByteStream(),
  360. self,
  361. trackId,
  362. currentPts,
  363. currentDts,
  364. discardEmulationPreventionBytes,
  365. readSequenceParameterSet,
  366. skipScalingList;
  367. H264Stream.prototype.init.call(this);
  368. self = this;
  369. this.push = function(packet) {
  370. if (packet.type !== 'video') {
  371. return;
  372. }
  373. trackId = packet.trackId;
  374. currentPts = packet.pts;
  375. currentDts = packet.dts;
  376. nalByteStream.push(packet);
  377. };
  378. nalByteStream.on('data', function(data) {
  379. var
  380. event = {
  381. trackId: trackId,
  382. pts: currentPts,
  383. dts: currentDts,
  384. data: data
  385. };
  386. switch (data[0] & 0x1f) {
  387. case 0x05:
  388. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  389. break;
  390. case 0x06:
  391. event.nalUnitType = 'sei_rbsp';
  392. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  393. break;
  394. case 0x07:
  395. event.nalUnitType = 'seq_parameter_set_rbsp';
  396. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  397. event.config = readSequenceParameterSet(event.escapedRBSP);
  398. break;
  399. case 0x08:
  400. event.nalUnitType = 'pic_parameter_set_rbsp';
  401. break;
  402. case 0x09:
  403. event.nalUnitType = 'access_unit_delimiter_rbsp';
  404. break;
  405. default:
  406. break;
  407. }
  408. self.trigger('data', event);
  409. });
  410. nalByteStream.on('done', function() {
  411. self.trigger('done');
  412. });
  413. this.flush = function() {
  414. nalByteStream.flush();
  415. };
  416. /**
  417. * Advance the ExpGolomb decoder past a scaling list. The scaling
  418. * list is optionally transmitted as part of a sequence parameter
  419. * set and is not relevant to transmuxing.
  420. * @param count {number} the number of entries in this scaling list
  421. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  422. * start of a scaling list
  423. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  424. */
  425. skipScalingList = function(count, expGolombDecoder) {
  426. var
  427. lastScale = 8,
  428. nextScale = 8,
  429. j,
  430. deltaScale;
  431. for (j = 0; j < count; j++) {
  432. if (nextScale !== 0) {
  433. deltaScale = expGolombDecoder.readExpGolomb();
  434. nextScale = (lastScale + deltaScale + 256) % 256;
  435. }
  436. lastScale = (nextScale === 0) ? lastScale : nextScale;
  437. }
  438. };
  439. /**
  440. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  441. * Sequence Payload"
  442. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  443. * unit
  444. * @return {Uint8Array} the RBSP without any Emulation
  445. * Prevention Bytes
  446. */
  447. discardEmulationPreventionBytes = function(data) {
  448. var
  449. length = data.byteLength,
  450. emulationPreventionBytesPositions = [],
  451. i = 1,
  452. newLength, newData;
  453. // Find all `Emulation Prevention Bytes`
  454. while (i < length - 2) {
  455. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  456. emulationPreventionBytesPositions.push(i + 2);
  457. i += 2;
  458. } else {
  459. i++;
  460. }
  461. }
  462. // If no Emulation Prevention Bytes were found just return the original
  463. // array
  464. if (emulationPreventionBytesPositions.length === 0) {
  465. return data;
  466. }
  467. // Create a new array to hold the NAL unit data
  468. newLength = length - emulationPreventionBytesPositions.length;
  469. newData = new Uint8Array(newLength);
  470. var sourceIndex = 0;
  471. for (i = 0; i < newLength; sourceIndex++, i++) {
  472. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  473. // Skip this byte
  474. sourceIndex++;
  475. // Remove this position index
  476. emulationPreventionBytesPositions.shift();
  477. }
  478. newData[i] = data[sourceIndex];
  479. }
  480. return newData;
  481. };
  482. /**
  483. * Read a sequence parameter set and return some interesting video
  484. * properties. A sequence parameter set is the H264 metadata that
  485. * describes the properties of upcoming video frames.
  486. * @param data {Uint8Array} the bytes of a sequence parameter set
  487. * @return {object} an object with configuration parsed from the
  488. * sequence parameter set, including the dimensions of the
  489. * associated video frames.
  490. */
  491. readSequenceParameterSet = function(data) {
  492. var
  493. frameCropLeftOffset = 0,
  494. frameCropRightOffset = 0,
  495. frameCropTopOffset = 0,
  496. frameCropBottomOffset = 0,
  497. sarScale = 1,
  498. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  499. chromaFormatIdc, picOrderCntType,
  500. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  501. picHeightInMapUnitsMinus1,
  502. frameMbsOnlyFlag,
  503. scalingListCount,
  504. sarRatio,
  505. aspectRatioIdc,
  506. i;
  507. expGolombDecoder = new ExpGolomb(data);
  508. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  509. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  510. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  511. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  512. // some profiles have more optional data we don't need
  513. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  514. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  515. if (chromaFormatIdc === 3) {
  516. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  517. }
  518. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  519. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  520. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  521. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  522. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  523. for (i = 0; i < scalingListCount; i++) {
  524. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  525. if (i < 6) {
  526. skipScalingList(16, expGolombDecoder);
  527. } else {
  528. skipScalingList(64, expGolombDecoder);
  529. }
  530. }
  531. }
  532. }
  533. }
  534. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  535. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  536. if (picOrderCntType === 0) {
  537. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  538. } else if (picOrderCntType === 1) {
  539. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  540. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  541. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  542. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  543. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  544. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  545. }
  546. }
  547. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  548. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  549. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  550. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  551. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  552. if (frameMbsOnlyFlag === 0) {
  553. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  554. }
  555. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  556. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  557. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  558. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  559. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  560. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  561. }
  562. if (expGolombDecoder.readBoolean()) {
  563. // vui_parameters_present_flag
  564. if (expGolombDecoder.readBoolean()) {
  565. // aspect_ratio_info_present_flag
  566. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  567. switch (aspectRatioIdc) {
  568. case 1: sarRatio = [1, 1]; break;
  569. case 2: sarRatio = [12, 11]; break;
  570. case 3: sarRatio = [10, 11]; break;
  571. case 4: sarRatio = [16, 11]; break;
  572. case 5: sarRatio = [40, 33]; break;
  573. case 6: sarRatio = [24, 11]; break;
  574. case 7: sarRatio = [20, 11]; break;
  575. case 8: sarRatio = [32, 11]; break;
  576. case 9: sarRatio = [80, 33]; break;
  577. case 10: sarRatio = [18, 11]; break;
  578. case 11: sarRatio = [15, 11]; break;
  579. case 12: sarRatio = [64, 33]; break;
  580. case 13: sarRatio = [160, 99]; break;
  581. case 14: sarRatio = [4, 3]; break;
  582. case 15: sarRatio = [3, 2]; break;
  583. case 16: sarRatio = [2, 1]; break;
  584. case 255: {
  585. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  586. expGolombDecoder.readUnsignedByte(),
  587. expGolombDecoder.readUnsignedByte() << 8 |
  588. expGolombDecoder.readUnsignedByte() ];
  589. break;
  590. }
  591. }
  592. if (sarRatio) {
  593. sarScale = sarRatio[0] / sarRatio[1];
  594. }
  595. }
  596. }
  597. return {
  598. profileIdc: profileIdc,
  599. levelIdc: levelIdc,
  600. profileCompatibility: profileCompatibility,
  601. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  602. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  603. };
  604. };
  605. };
  606. H264Stream.prototype = new Stream();
  607. module.exports = {
  608. H264Stream: H264Stream,
  609. NalByteStream: NalByteStream
  610. };
  611. },{"../utils/exp-golomb.js":14,"../utils/stream.js":15}],4:[function(require,module,exports){
  612. var highPrefix = [33, 16, 5, 32, 164, 27];
  613. var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
  614. var zeroFill = function(count) {
  615. var a = [];
  616. while (count--) {
  617. a.push(0);
  618. }
  619. return a;
  620. };
  621. var makeTable = function(metaTable) {
  622. return Object.keys(metaTable).reduce(function(obj, key) {
  623. obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
  624. return arr.concat(part);
  625. }, []));
  626. return obj;
  627. }, {});
  628. };
  629. // Frames-of-silence to use for filling in missing AAC frames
  630. var coneOfSilence = {
  631. 96000: [highPrefix, [227, 64], zeroFill(154), [56]],
  632. 88200: [highPrefix, [231], zeroFill(170), [56]],
  633. 64000: [highPrefix, [248, 192], zeroFill(240), [56]],
  634. 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
  635. 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
  636. 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
  637. 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
  638. 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
  639. 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]],
  640. 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]],
  641. 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
  642. };
  643. module.exports = makeTable(coneOfSilence);
  644. },{}],5:[function(require,module,exports){
  645. /**
  646. * mux.js
  647. *
  648. * Copyright (c) 2015 Brightcove
  649. * All rights reserved.
  650. *
  651. * Reads in-band caption information from a video elementary
  652. * stream. Captions must follow the CEA-708 standard for injection
  653. * into an MPEG-2 transport streams.
  654. * @see https://en.wikipedia.org/wiki/CEA-708
  655. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  656. */
  657. 'use strict';
  658. // -----------------
  659. // Link To Transport
  660. // -----------------
  661. // Supplemental enhancement information (SEI) NAL units have a
  662. // payload type field to indicate how they are to be
  663. // interpreted. CEAS-708 caption content is always transmitted with
  664. // payload type 0x04.
  665. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  666. RBSP_TRAILING_BITS = 128,
  667. Stream = require('../utils/stream');
  668. /**
  669. * Parse a supplemental enhancement information (SEI) NAL unit.
  670. * Stops parsing once a message of type ITU T T35 has been found.
  671. *
  672. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  673. * @return {object} the parsed SEI payload
  674. * @see Rec. ITU-T H.264, 7.3.2.3.1
  675. */
  676. var parseSei = function(bytes) {
  677. var
  678. i = 0,
  679. result = {
  680. payloadType: -1,
  681. payloadSize: 0
  682. },
  683. payloadType = 0,
  684. payloadSize = 0;
  685. // go through the sei_rbsp parsing each each individual sei_message
  686. while (i < bytes.byteLength) {
  687. // stop once we have hit the end of the sei_rbsp
  688. if (bytes[i] === RBSP_TRAILING_BITS) {
  689. break;
  690. }
  691. // Parse payload type
  692. while (bytes[i] === 0xFF) {
  693. payloadType += 255;
  694. i++;
  695. }
  696. payloadType += bytes[i++];
  697. // Parse payload size
  698. while (bytes[i] === 0xFF) {
  699. payloadSize += 255;
  700. i++;
  701. }
  702. payloadSize += bytes[i++];
  703. // this sei_message is a 608/708 caption so save it and break
  704. // there can only ever be one caption message in a frame's sei
  705. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  706. result.payloadType = payloadType;
  707. result.payloadSize = payloadSize;
  708. result.payload = bytes.subarray(i, i + payloadSize);
  709. break;
  710. }
  711. // skip the payload and parse the next message
  712. i += payloadSize;
  713. payloadType = 0;
  714. payloadSize = 0;
  715. }
  716. return result;
  717. };
  718. // see ANSI/SCTE 128-1 (2013), section 8.1
  719. var parseUserData = function(sei) {
  720. // itu_t_t35_contry_code must be 181 (United States) for
  721. // captions
  722. if (sei.payload[0] !== 181) {
  723. return null;
  724. }
  725. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  726. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  727. return null;
  728. }
  729. // the user_identifier should be "GA94" to indicate ATSC1 data
  730. if (String.fromCharCode(sei.payload[3],
  731. sei.payload[4],
  732. sei.payload[5],
  733. sei.payload[6]) !== 'GA94') {
  734. return null;
  735. }
  736. // finally, user_data_type_code should be 0x03 for caption data
  737. if (sei.payload[7] !== 0x03) {
  738. return null;
  739. }
  740. // return the user_data_type_structure and strip the trailing
  741. // marker bits
  742. return sei.payload.subarray(8, sei.payload.length - 1);
  743. };
  744. // see CEA-708-D, section 4.4
  745. var parseCaptionPackets = function(pts, userData) {
  746. var results = [], i, count, offset, data;
  747. // if this is just filler, return immediately
  748. if (!(userData[0] & 0x40)) {
  749. return results;
  750. }
  751. // parse out the cc_data_1 and cc_data_2 fields
  752. count = userData[0] & 0x1f;
  753. for (i = 0; i < count; i++) {
  754. offset = i * 3;
  755. data = {
  756. type: userData[offset + 2] & 0x03,
  757. pts: pts
  758. };
  759. // capture cc data when cc_valid is 1
  760. if (userData[offset + 2] & 0x04) {
  761. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  762. results.push(data);
  763. }
  764. }
  765. return results;
  766. };
  767. var CaptionStream = function() {
  768. CaptionStream.prototype.init.call(this);
  769. this.captionPackets_ = [];
  770. this.ccStreams_ = [
  771. new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
  772. new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
  773. new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
  774. new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
  775. ];
  776. this.reset();
  777. // forward data and done events from CCs to this CaptionStream
  778. this.ccStreams_.forEach(function(cc) {
  779. cc.on('data', this.trigger.bind(this, 'data'));
  780. cc.on('done', this.trigger.bind(this, 'done'));
  781. }, this);
  782. };
  783. CaptionStream.prototype = new Stream();
  784. CaptionStream.prototype.push = function(event) {
  785. var sei, userData;
  786. // only examine SEI NALs
  787. if (event.nalUnitType !== 'sei_rbsp') {
  788. return;
  789. }
  790. // parse the sei
  791. sei = parseSei(event.escapedRBSP);
  792. // ignore everything but user_data_registered_itu_t_t35
  793. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  794. return;
  795. }
  796. // parse out the user data payload
  797. userData = parseUserData(sei);
  798. // ignore unrecognized userData
  799. if (!userData) {
  800. return;
  801. }
  802. // Sometimes, the same segment # will be downloaded twice. To stop the
  803. // caption data from being processed twice, we track the latest dts we've
  804. // received and ignore everything with a dts before that. However, since
  805. // data for a specific dts can be split across 2 packets on either side of
  806. // a segment boundary, we need to make sure we *don't* ignore the second
  807. // dts packet we receive that has dts === this.latestDts_. And thus, the
  808. // ignoreNextEqualDts_ flag was born.
  809. if (event.dts < this.latestDts_) {
  810. // We've started getting older data, so set the flag.
  811. this.ignoreNextEqualDts_ = true;
  812. return;
  813. } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
  814. // We've received the last duplicate packet, time to start processing again
  815. this.ignoreNextEqualDts_ = false;
  816. return;
  817. }
  818. // parse out CC data packets and save them for later
  819. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  820. this.latestDts_ = event.dts;
  821. };
  822. CaptionStream.prototype.flush = function() {
  823. // make sure we actually parsed captions before proceeding
  824. if (!this.captionPackets_.length) {
  825. this.ccStreams_.forEach(function(cc) {
  826. cc.flush();
  827. }, this);
  828. return;
  829. }
  830. // In Chrome, the Array#sort function is not stable so add a
  831. // presortIndex that we can use to ensure we get a stable-sort
  832. this.captionPackets_.forEach(function(elem, idx) {
  833. elem.presortIndex = idx;
  834. });
  835. // sort caption byte-pairs based on their PTS values
  836. this.captionPackets_.sort(function(a, b) {
  837. if (a.pts === b.pts) {
  838. return a.presortIndex - b.presortIndex;
  839. }
  840. return a.pts - b.pts;
  841. });
  842. this.captionPackets_.forEach(function(packet) {
  843. if (packet.type < 2) {
  844. // Dispatch packet to the right Cea608Stream
  845. this.dispatchCea608Packet(packet);
  846. }
  847. // this is where an 'else' would go for a dispatching packets
  848. // to a theoretical Cea708Stream that handles SERVICEn data
  849. }, this);
  850. this.captionPackets_.length = 0;
  851. this.ccStreams_.forEach(function(cc) {
  852. cc.flush();
  853. }, this);
  854. return;
  855. };
  856. CaptionStream.prototype.reset = function() {
  857. this.latestDts_ = null;
  858. this.ignoreNextEqualDts_ = false;
  859. this.activeCea608Channel_ = [null, null];
  860. this.ccStreams_.forEach(function(ccStream) {
  861. ccStream.reset();
  862. });
  863. };
  864. CaptionStream.prototype.dispatchCea608Packet = function(packet) {
  865. // NOTE: packet.type is the CEA608 field
  866. if (this.setsChannel1Active(packet)) {
  867. this.activeCea608Channel_[packet.type] = 0;
  868. } else if (this.setsChannel2Active(packet)) {
  869. this.activeCea608Channel_[packet.type] = 1;
  870. }
  871. if (this.activeCea608Channel_[packet.type] === null) {
  872. // If we haven't received anything to set the active channel, discard the
  873. // data; we don't want jumbled captions
  874. return;
  875. }
  876. this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
  877. };
  878. CaptionStream.prototype.setsChannel1Active = function(packet) {
  879. return ((packet.ccData & 0x7800) === 0x1000);
  880. };
  881. CaptionStream.prototype.setsChannel2Active = function(packet) {
  882. return ((packet.ccData & 0x7800) === 0x1800);
  883. };
  884. // ----------------------
  885. // Session to Application
  886. // ----------------------
  887. var CHARACTER_TRANSLATION = {
  888. 0x2a: 0xe1, // á
  889. 0x5c: 0xe9, // é
  890. 0x5e: 0xed, // í
  891. 0x5f: 0xf3, // ó
  892. 0x60: 0xfa, // ú
  893. 0x7b: 0xe7, // ç
  894. 0x7c: 0xf7, // ÷
  895. 0x7d: 0xd1, // Ñ
  896. 0x7e: 0xf1, // ñ
  897. 0x7f: 0x2588, // █
  898. 0x0130: 0xae, // ®
  899. 0x0131: 0xb0, // °
  900. 0x0132: 0xbd, // ½
  901. 0x0133: 0xbf, // ¿
  902. 0x0134: 0x2122, // ™
  903. 0x0135: 0xa2, // ¢
  904. 0x0136: 0xa3, // £
  905. 0x0137: 0x266a, // ♪
  906. 0x0138: 0xe0, // à
  907. 0x0139: 0xa0, //
  908. 0x013a: 0xe8, // è
  909. 0x013b: 0xe2, // â
  910. 0x013c: 0xea, // ê
  911. 0x013d: 0xee, // î
  912. 0x013e: 0xf4, // ô
  913. 0x013f: 0xfb, // û
  914. 0x0220: 0xc1, // Á
  915. 0x0221: 0xc9, // É
  916. 0x0222: 0xd3, // Ó
  917. 0x0223: 0xda, // Ú
  918. 0x0224: 0xdc, // Ü
  919. 0x0225: 0xfc, // ü
  920. 0x0226: 0x2018, // ‘
  921. 0x0227: 0xa1, // ¡
  922. 0x0228: 0x2a, // *
  923. 0x0229: 0x27, // '
  924. 0x022a: 0x2014, // —
  925. 0x022b: 0xa9, // ©
  926. 0x022c: 0x2120, // ℠
  927. 0x022d: 0x2022, // •
  928. 0x022e: 0x201c, // “
  929. 0x022f: 0x201d, // ”
  930. 0x0230: 0xc0, // À
  931. 0x0231: 0xc2, // Â
  932. 0x0232: 0xc7, // Ç
  933. 0x0233: 0xc8, // È
  934. 0x0234: 0xca, // Ê
  935. 0x0235: 0xcb, // Ë
  936. 0x0236: 0xeb, // ë
  937. 0x0237: 0xce, // Î
  938. 0x0238: 0xcf, // Ï
  939. 0x0239: 0xef, // ï
  940. 0x023a: 0xd4, // Ô
  941. 0x023b: 0xd9, // Ù
  942. 0x023c: 0xf9, // ù
  943. 0x023d: 0xdb, // Û
  944. 0x023e: 0xab, // «
  945. 0x023f: 0xbb, // »
  946. 0x0320: 0xc3, // Ã
  947. 0x0321: 0xe3, // ã
  948. 0x0322: 0xcd, // Í
  949. 0x0323: 0xcc, // Ì
  950. 0x0324: 0xec, // ì
  951. 0x0325: 0xd2, // Ò
  952. 0x0326: 0xf2, // ò
  953. 0x0327: 0xd5, // Õ
  954. 0x0328: 0xf5, // õ
  955. 0x0329: 0x7b, // {
  956. 0x032a: 0x7d, // }
  957. 0x032b: 0x5c, // \
  958. 0x032c: 0x5e, // ^
  959. 0x032d: 0x5f, // _
  960. 0x032e: 0x7c, // |
  961. 0x032f: 0x7e, // ~
  962. 0x0330: 0xc4, // Ä
  963. 0x0331: 0xe4, // ä
  964. 0x0332: 0xd6, // Ö
  965. 0x0333: 0xf6, // ö
  966. 0x0334: 0xdf, // ß
  967. 0x0335: 0xa5, // ¥
  968. 0x0336: 0xa4, // ¤
  969. 0x0337: 0x2502, // │
  970. 0x0338: 0xc5, // Å
  971. 0x0339: 0xe5, // å
  972. 0x033a: 0xd8, // Ø
  973. 0x033b: 0xf8, // ø
  974. 0x033c: 0x250c, // ┌
  975. 0x033d: 0x2510, // ┐
  976. 0x033e: 0x2514, // └
  977. 0x033f: 0x2518 // ┘
  978. };
  979. var getCharFromCode = function(code) {
  980. if (code === null) {
  981. return '';
  982. }
  983. code = CHARACTER_TRANSLATION[code] || code;
  984. return String.fromCharCode(code);
  985. };
  986. // the index of the last row in a CEA-608 display buffer
  987. var BOTTOM_ROW = 14;
  988. // This array is used for mapping PACs -> row #, since there's no way of
  989. // getting it through bit logic.
  990. var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
  991. 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
  992. // CEA-608 captions are rendered onto a 34x15 matrix of character
  993. // cells. The "bottom" row is the last element in the outer array.
  994. var createDisplayBuffer = function() {
  995. var result = [], i = BOTTOM_ROW + 1;
  996. while (i--) {
  997. result.push('');
  998. }
  999. return result;
  1000. };
  1001. var Cea608Stream = function(field, dataChannel) {
  1002. Cea608Stream.prototype.init.call(this);
  1003. this.field_ = field || 0;
  1004. this.dataChannel_ = dataChannel || 0;
  1005. this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
  1006. this.setConstants();
  1007. this.reset();
  1008. this.push = function(packet) {
  1009. var data, swap, char0, char1, text;
  1010. // remove the parity bits
  1011. data = packet.ccData & 0x7f7f;
  1012. // ignore duplicate control codes; the spec demands they're sent twice
  1013. if (data === this.lastControlCode_) {
  1014. this.lastControlCode_ = null;
  1015. return;
  1016. }
  1017. // Store control codes
  1018. if ((data & 0xf000) === 0x1000) {
  1019. this.lastControlCode_ = data;
  1020. } else if (data !== this.PADDING_) {
  1021. this.lastControlCode_ = null;
  1022. }
  1023. char0 = data >>> 8;
  1024. char1 = data & 0xff;
  1025. if (data === this.PADDING_) {
  1026. return;
  1027. } else if (data === this.RESUME_CAPTION_LOADING_) {
  1028. this.mode_ = 'popOn';
  1029. } else if (data === this.END_OF_CAPTION_) {
  1030. this.clearFormatting(packet.pts);
  1031. // if a caption was being displayed, it's gone now
  1032. this.flushDisplayed(packet.pts);
  1033. // flip memory
  1034. swap = this.displayed_;
  1035. this.displayed_ = this.nonDisplayed_;
  1036. this.nonDisplayed_ = swap;
  1037. // start measuring the time to display the caption
  1038. this.startPts_ = packet.pts;
  1039. } else if (data === this.ROLL_UP_2_ROWS_) {
  1040. this.topRow_ = BOTTOM_ROW - 1;
  1041. this.mode_ = 'rollUp';
  1042. } else if (data === this.ROLL_UP_3_ROWS_) {
  1043. this.topRow_ = BOTTOM_ROW - 2;
  1044. this.mode_ = 'rollUp';
  1045. } else if (data === this.ROLL_UP_4_ROWS_) {
  1046. this.topRow_ = BOTTOM_ROW - 3;
  1047. this.mode_ = 'rollUp';
  1048. } else if (data === this.CARRIAGE_RETURN_) {
  1049. this.clearFormatting(packet.pts);
  1050. this.flushDisplayed(packet.pts);
  1051. this.shiftRowsUp_();
  1052. this.startPts_ = packet.pts;
  1053. } else if (data === this.BACKSPACE_) {
  1054. if (this.mode_ === 'popOn') {
  1055. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  1056. } else {
  1057. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  1058. }
  1059. } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
  1060. this.flushDisplayed(packet.pts);
  1061. this.displayed_ = createDisplayBuffer();
  1062. } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
  1063. this.nonDisplayed_ = createDisplayBuffer();
  1064. } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
  1065. this.mode_ = 'paintOn';
  1066. // Append special characters to caption text
  1067. } else if (this.isSpecialCharacter(char0, char1)) {
  1068. // Bitmask char0 so that we can apply character transformations
  1069. // regardless of field and data channel.
  1070. // Then byte-shift to the left and OR with char1 so we can pass the
  1071. // entire character code to `getCharFromCode`.
  1072. char0 = (char0 & 0x03) << 8;
  1073. text = getCharFromCode(char0 | char1);
  1074. this[this.mode_](packet.pts, text);
  1075. this.column_++;
  1076. // Append extended characters to caption text
  1077. } else if (this.isExtCharacter(char0, char1)) {
  1078. // Extended characters always follow their "non-extended" equivalents.
  1079. // IE if a "è" is desired, you'll always receive "eè"; non-compliant
  1080. // decoders are supposed to drop the "è", while compliant decoders
  1081. // backspace the "e" and insert "è".
  1082. // Delete the previous character
  1083. if (this.mode_ === 'popOn') {
  1084. this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
  1085. } else {
  1086. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  1087. }
  1088. // Bitmask char0 so that we can apply character transformations
  1089. // regardless of field and data channel.
  1090. // Then byte-shift to the left and OR with char1 so we can pass the
  1091. // entire character code to `getCharFromCode`.
  1092. char0 = (char0 & 0x03) << 8;
  1093. text = getCharFromCode(char0 | char1);
  1094. this[this.mode_](packet.pts, text);
  1095. this.column_++;
  1096. // Process mid-row codes
  1097. } else if (this.isMidRowCode(char0, char1)) {
  1098. // Attributes are not additive, so clear all formatting
  1099. this.clearFormatting(packet.pts);
  1100. // According to the standard, mid-row codes
  1101. // should be replaced with spaces, so add one now
  1102. this[this.mode_](packet.pts, ' ');
  1103. this.column_++;
  1104. if ((char1 & 0xe) === 0xe) {
  1105. this.addFormatting(packet.pts, ['i']);
  1106. }
  1107. if ((char1 & 0x1) === 0x1) {
  1108. this.addFormatting(packet.pts, ['u']);
  1109. }
  1110. // Detect offset control codes and adjust cursor
  1111. } else if (this.isOffsetControlCode(char0, char1)) {
  1112. // Cursor position is set by indent PAC (see below) in 4-column
  1113. // increments, with an additional offset code of 1-3 to reach any
  1114. // of the 32 columns specified by CEA-608. So all we need to do
  1115. // here is increment the column cursor by the given offset.
  1116. this.column_ += (char1 & 0x03);
  1117. // Detect PACs (Preamble Address Codes)
  1118. } else if (this.isPAC(char0, char1)) {
  1119. // There's no logic for PAC -> row mapping, so we have to just
  1120. // find the row code in an array and use its index :(
  1121. var row = ROWS.indexOf(data & 0x1f20);
  1122. if (row !== this.row_) {
  1123. // formatting is only persistent for current row
  1124. this.clearFormatting(packet.pts);
  1125. this.row_ = row;
  1126. }
  1127. // All PACs can apply underline, so detect and apply
  1128. // (All odd-numbered second bytes set underline)
  1129. if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
  1130. this.addFormatting(packet.pts, ['u']);
  1131. }
  1132. if ((data & 0x10) === 0x10) {
  1133. // We've got an indent level code. Each successive even number
  1134. // increments the column cursor by 4, so we can get the desired
  1135. // column position by bit-shifting to the right (to get n/2)
  1136. // and multiplying by 4.
  1137. this.column_ = ((data & 0xe) >> 1) * 4;
  1138. }
  1139. if (this.isColorPAC(char1)) {
  1140. // it's a color code, though we only support white, which
  1141. // can be either normal or italicized. white italics can be
  1142. // either 0x4e or 0x6e depending on the row, so we just
  1143. // bitwise-and with 0xe to see if italics should be turned on
  1144. if ((char1 & 0xe) === 0xe) {
  1145. this.addFormatting(packet.pts, ['i']);
  1146. }
  1147. }
  1148. // We have a normal character in char0, and possibly one in char1
  1149. } else if (this.isNormalChar(char0)) {
  1150. if (char1 === 0x00) {
  1151. char1 = null;
  1152. }
  1153. text = getCharFromCode(char0);
  1154. text += getCharFromCode(char1);
  1155. this[this.mode_](packet.pts, text);
  1156. this.column_ += text.length;
  1157. } // finish data processing
  1158. };
  1159. };
  1160. Cea608Stream.prototype = new Stream();
  1161. // Trigger a cue point that captures the current state of the
  1162. // display buffer
  1163. Cea608Stream.prototype.flushDisplayed = function(pts) {
  1164. var content = this.displayed_
  1165. // remove spaces from the start and end of the string
  1166. .map(function(row) {
  1167. return row.trim();
  1168. })
  1169. // combine all text rows to display in one cue
  1170. .join('\n')
  1171. // and remove blank rows from the start and end, but not the middle
  1172. .replace(/^\n+|\n+$/g, '');
  1173. if (content.length) {
  1174. this.trigger('data', {
  1175. startPts: this.startPts_,
  1176. endPts: pts,
  1177. text: content,
  1178. stream: this.name_
  1179. });
  1180. }
  1181. };
  1182. /**
  1183. * Zero out the data, used for startup and on seek
  1184. */
  1185. Cea608Stream.prototype.reset = function() {
  1186. this.mode_ = 'popOn';
  1187. // When in roll-up mode, the index of the last row that will
  1188. // actually display captions. If a caption is shifted to a row
  1189. // with a lower index than this, it is cleared from the display
  1190. // buffer
  1191. this.topRow_ = 0;
  1192. this.startPts_ = 0;
  1193. this.displayed_ = createDisplayBuffer();
  1194. this.nonDisplayed_ = createDisplayBuffer();
  1195. this.lastControlCode_ = null;
  1196. // Track row and column for proper line-breaking and spacing
  1197. this.column_ = 0;
  1198. this.row_ = BOTTOM_ROW;
  1199. // This variable holds currently-applied formatting
  1200. this.formatting_ = [];
  1201. };
  1202. /**
  1203. * Sets up control code and related constants for this instance
  1204. */
  1205. Cea608Stream.prototype.setConstants = function() {
  1206. // The following attributes have these uses:
  1207. // ext_ : char0 for mid-row codes, and the base for extended
  1208. // chars (ext_+0, ext_+1, and ext_+2 are char0s for
  1209. // extended codes)
  1210. // control_: char0 for control codes, except byte-shifted to the
  1211. // left so that we can do this.control_ | CONTROL_CODE
  1212. // offset_: char0 for tab offset codes
  1213. //
  1214. // It's also worth noting that control codes, and _only_ control codes,
  1215. // differ between field 1 and field2. Field 2 control codes are always
  1216. // their field 1 value plus 1. That's why there's the "| field" on the
  1217. // control value.
  1218. if (this.dataChannel_ === 0) {
  1219. this.BASE_ = 0x10;
  1220. this.EXT_ = 0x11;
  1221. this.CONTROL_ = (0x14 | this.field_) << 8;
  1222. this.OFFSET_ = 0x17;
  1223. } else if (this.dataChannel_ === 1) {
  1224. this.BASE_ = 0x18;
  1225. this.EXT_ = 0x19;
  1226. this.CONTROL_ = (0x1c | this.field_) << 8;
  1227. this.OFFSET_ = 0x1f;
  1228. }
  1229. // Constants for the LSByte command codes recognized by Cea608Stream. This
  1230. // list is not exhaustive. For a more comprehensive listing and semantics see
  1231. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  1232. // Padding
  1233. this.PADDING_ = 0x0000;
  1234. // Pop-on Mode
  1235. this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
  1236. this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
  1237. // Roll-up Mode
  1238. this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
  1239. this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
  1240. this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
  1241. this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
  1242. // paint-on mode (not supported)
  1243. this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
  1244. // Erasure
  1245. this.BACKSPACE_ = this.CONTROL_ | 0x21;
  1246. this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
  1247. this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
  1248. };
  1249. /**
  1250. * Detects if the 2-byte packet data is a special character
  1251. *
  1252. * Special characters have a second byte in the range 0x30 to 0x3f,
  1253. * with the first byte being 0x11 (for data channel 1) or 0x19 (for
  1254. * data channel 2).
  1255. *
  1256. * @param {Integer} char0 The first byte
  1257. * @param {Integer} char1 The second byte
  1258. * @return {Boolean} Whether the 2 bytes are an special character
  1259. */
  1260. Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
  1261. return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
  1262. };
  1263. /**
  1264. * Detects if the 2-byte packet data is an extended character
  1265. *
  1266. * Extended characters have a second byte in the range 0x20 to 0x3f,
  1267. * with the first byte being 0x12 or 0x13 (for data channel 1) or
  1268. * 0x1a or 0x1b (for data channel 2).
  1269. *
  1270. * @param {Integer} char0 The first byte
  1271. * @param {Integer} char1 The second byte
  1272. * @return {Boolean} Whether the 2 bytes are an extended character
  1273. */
  1274. Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
  1275. return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
  1276. (char1 >= 0x20 && char1 <= 0x3f));
  1277. };
  1278. /**
  1279. * Detects if the 2-byte packet is a mid-row code
  1280. *
  1281. * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
  1282. * the first byte being 0x11 (for data channel 1) or 0x19 (for data
  1283. * channel 2).
  1284. *
  1285. * @param {Integer} char0 The first byte
  1286. * @param {Integer} char1 The second byte
  1287. * @return {Boolean} Whether the 2 bytes are a mid-row code
  1288. */
  1289. Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
  1290. return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
  1291. };
  1292. /**
  1293. * Detects if the 2-byte packet is an offset control code
  1294. *
  1295. * Offset control codes have a second byte in the range 0x21 to 0x23,
  1296. * with the first byte being 0x17 (for data channel 1) or 0x1f (for
  1297. * data channel 2).
  1298. *
  1299. * @param {Integer} char0 The first byte
  1300. * @param {Integer} char1 The second byte
  1301. * @return {Boolean} Whether the 2 bytes are an offset control code
  1302. */
  1303. Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
  1304. return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
  1305. };
  1306. /**
  1307. * Detects if the 2-byte packet is a Preamble Address Code
  1308. *
  1309. * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
  1310. * or 0x18 to 0x1f (for data channel 2), with the second byte in the
  1311. * range 0x40 to 0x7f.
  1312. *
  1313. * @param {Integer} char0 The first byte
  1314. * @param {Integer} char1 The second byte
  1315. * @return {Boolean} Whether the 2 bytes are a PAC
  1316. */
  1317. Cea608Stream.prototype.isPAC = function(char0, char1) {
  1318. return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
  1319. (char1 >= 0x40 && char1 <= 0x7f));
  1320. };
  1321. /**
  1322. * Detects if a packet's second byte is in the range of a PAC color code
  1323. *
  1324. * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
  1325. * 0x60 to 0x6f.
  1326. *
  1327. * @param {Integer} char1 The second byte
  1328. * @return {Boolean} Whether the byte is a color PAC
  1329. */
  1330. Cea608Stream.prototype.isColorPAC = function(char1) {
  1331. return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
  1332. };
  1333. /**
  1334. * Detects if a single byte is in the range of a normal character
  1335. *
  1336. * Normal text bytes are in the range 0x20 to 0x7f.
  1337. *
  1338. * @param {Integer} char The byte
  1339. * @return {Boolean} Whether the byte is a normal character
  1340. */
  1341. Cea608Stream.prototype.isNormalChar = function(char) {
  1342. return (char >= 0x20 && char <= 0x7f);
  1343. };
  1344. // Adds the opening HTML tag for the passed character to the caption text,
  1345. // and keeps track of it for later closing
  1346. Cea608Stream.prototype.addFormatting = function(pts, format) {
  1347. this.formatting_ = this.formatting_.concat(format);
  1348. var text = format.reduce(function(text, format) {
  1349. return text + '<' + format + '>';
  1350. }, '');
  1351. this[this.mode_](pts, text);
  1352. };
  1353. // Adds HTML closing tags for current formatting to caption text and
  1354. // clears remembered formatting
  1355. Cea608Stream.prototype.clearFormatting = function(pts) {
  1356. if (!this.formatting_.length) {
  1357. return;
  1358. }
  1359. var text = this.formatting_.reverse().reduce(function(text, format) {
  1360. return text + '</' + format + '>';
  1361. }, '');
  1362. this.formatting_ = [];
  1363. this[this.mode_](pts, text);
  1364. };
  1365. // Mode Implementations
  1366. Cea608Stream.prototype.popOn = function(pts, text) {
  1367. var baseRow = this.nonDisplayed_[this.row_];
  1368. // buffer characters
  1369. baseRow += text;
  1370. this.nonDisplayed_[this.row_] = baseRow;
  1371. };
  1372. Cea608Stream.prototype.rollUp = function(pts, text) {
  1373. var baseRow = this.displayed_[BOTTOM_ROW];
  1374. baseRow += text;
  1375. this.displayed_[BOTTOM_ROW] = baseRow;
  1376. };
  1377. Cea608Stream.prototype.shiftRowsUp_ = function() {
  1378. var i;
  1379. // clear out inactive rows
  1380. for (i = 0; i < this.topRow_; i++) {
  1381. this.displayed_[i] = '';
  1382. }
  1383. // shift displayed rows up
  1384. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  1385. this.displayed_[i] = this.displayed_[i + 1];
  1386. }
  1387. // clear out the bottom row
  1388. this.displayed_[BOTTOM_ROW] = '';
  1389. };
  1390. // paintOn mode is not implemented
  1391. Cea608Stream.prototype.paintOn = function() {};
  1392. // exports
  1393. module.exports = {
  1394. CaptionStream: CaptionStream,
  1395. Cea608Stream: Cea608Stream
  1396. };
  1397. },{"../utils/stream":15}],6:[function(require,module,exports){
  1398. /**
  1399. * mux.js
  1400. *
  1401. * Copyright (c) 2015 Brightcove
  1402. * All rights reserved.
  1403. *
  1404. * A stream-based mp2t to mp4 converter. This utility can be used to
  1405. * deliver mp4s to a SourceBuffer on platforms that support native
  1406. * Media Source Extensions.
  1407. */
  1408. 'use strict';
  1409. var Stream = require('../utils/stream.js'),
  1410. CaptionStream = require('./caption-stream'),
  1411. StreamTypes = require('./stream-types'),
  1412. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  1413. var m2tsStreamTypes = require('./stream-types.js');
  1414. // object types
  1415. var TransportPacketStream, TransportParseStream, ElementaryStream;
  1416. // constants
  1417. var
  1418. MP2T_PACKET_LENGTH = 188, // bytes
  1419. SYNC_BYTE = 0x47;
  1420. /**
  1421. * Splits an incoming stream of binary data into MPEG-2 Transport
  1422. * Stream packets.
  1423. */
  1424. TransportPacketStream = function() {
  1425. var
  1426. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  1427. bytesInBuffer = 0;
  1428. TransportPacketStream.prototype.init.call(this);
  1429. // Deliver new bytes to the stream.
  1430. this.push = function(bytes) {
  1431. var
  1432. startIndex = 0,
  1433. endIndex = MP2T_PACKET_LENGTH,
  1434. everything;
  1435. // If there are bytes remaining from the last segment, prepend them to the
  1436. // bytes that were pushed in
  1437. if (bytesInBuffer) {
  1438. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  1439. everything.set(buffer.subarray(0, bytesInBuffer));
  1440. everything.set(bytes, bytesInBuffer);
  1441. bytesInBuffer = 0;
  1442. } else {
  1443. everything = bytes;
  1444. }
  1445. // While we have enough data for a packet
  1446. while (endIndex < everything.byteLength) {
  1447. // Look for a pair of start and end sync bytes in the data..
  1448. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  1449. // We found a packet so emit it and jump one whole packet forward in
  1450. // the stream
  1451. this.trigger('data', everything.subarray(startIndex, endIndex));
  1452. startIndex += MP2T_PACKET_LENGTH;
  1453. endIndex += MP2T_PACKET_LENGTH;
  1454. continue;
  1455. }
  1456. // If we get here, we have somehow become de-synchronized and we need to step
  1457. // forward one byte at a time until we find a pair of sync bytes that denote
  1458. // a packet
  1459. startIndex++;
  1460. endIndex++;
  1461. }
  1462. // If there was some data left over at the end of the segment that couldn't
  1463. // possibly be a whole packet, keep it because it might be the start of a packet
  1464. // that continues in the next segment
  1465. if (startIndex < everything.byteLength) {
  1466. buffer.set(everything.subarray(startIndex), 0);
  1467. bytesInBuffer = everything.byteLength - startIndex;
  1468. }
  1469. };
  1470. this.flush = function() {
  1471. // If the buffer contains a whole packet when we are being flushed, emit it
  1472. // and empty the buffer. Otherwise hold onto the data because it may be
  1473. // important for decoding the next segment
  1474. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  1475. this.trigger('data', buffer);
  1476. bytesInBuffer = 0;
  1477. }
  1478. this.trigger('done');
  1479. };
  1480. };
  1481. TransportPacketStream.prototype = new Stream();
  1482. /**
  1483. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  1484. * forms of the individual transport stream packets.
  1485. */
  1486. TransportParseStream = function() {
  1487. var parsePsi, parsePat, parsePmt, self;
  1488. TransportParseStream.prototype.init.call(this);
  1489. self = this;
  1490. this.packetsWaitingForPmt = [];
  1491. this.programMapTable = undefined;
  1492. parsePsi = function(payload, psi) {
  1493. var offset = 0;
  1494. // PSI packets may be split into multiple sections and those
  1495. // sections may be split into multiple packets. If a PSI
  1496. // section starts in this packet, the payload_unit_start_indicator
  1497. // will be true and the first byte of the payload will indicate
  1498. // the offset from the current position to the start of the
  1499. // section.
  1500. if (psi.payloadUnitStartIndicator) {
  1501. offset += payload[offset] + 1;
  1502. }
  1503. if (psi.type === 'pat') {
  1504. parsePat(payload.subarray(offset), psi);
  1505. } else {
  1506. parsePmt(payload.subarray(offset), psi);
  1507. }
  1508. };
  1509. parsePat = function(payload, pat) {
  1510. pat.section_number = payload[7]; // eslint-disable-line camelcase
  1511. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  1512. // skip the PSI header and parse the first PMT entry
  1513. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  1514. pat.pmtPid = self.pmtPid;
  1515. };
  1516. /**
  1517. * Parse out the relevant fields of a Program Map Table (PMT).
  1518. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  1519. * packet. The first byte in this array should be the table_id
  1520. * field.
  1521. * @param pmt {object} the object that should be decorated with
  1522. * fields parsed from the PMT.
  1523. */
  1524. parsePmt = function(payload, pmt) {
  1525. var sectionLength, tableEnd, programInfoLength, offset;
  1526. // PMTs can be sent ahead of the time when they should actually
  1527. // take effect. We don't believe this should ever be the case
  1528. // for HLS but we'll ignore "forward" PMT declarations if we see
  1529. // them. Future PMT declarations have the current_next_indicator
  1530. // set to zero.
  1531. if (!(payload[5] & 0x01)) {
  1532. return;
  1533. }
  1534. // overwrite any existing program map table
  1535. self.programMapTable = {
  1536. video: null,
  1537. audio: null,
  1538. 'timed-metadata': {}
  1539. };
  1540. // the mapping table ends at the end of the current section
  1541. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  1542. tableEnd = 3 + sectionLength - 4;
  1543. // to determine where the table is, we have to figure out how
  1544. // long the program info descriptors are
  1545. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  1546. // advance the offset to the first entry in the mapping table
  1547. offset = 12 + programInfoLength;
  1548. while (offset < tableEnd) {
  1549. var streamType = payload[offset];
  1550. var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
  1551. // only map a single elementary_pid for audio and video stream types
  1552. // TODO: should this be done for metadata too? for now maintain behavior of
  1553. // multiple metadata streams
  1554. if (streamType === StreamTypes.H264_STREAM_TYPE &&
  1555. self.programMapTable.video === null) {
  1556. self.programMapTable.video = pid;
  1557. } else if (streamType === StreamTypes.ADTS_STREAM_TYPE &&
  1558. self.programMapTable.audio === null) {
  1559. self.programMapTable.audio = pid;
  1560. } else if (streamType === StreamTypes.METADATA_STREAM_TYPE) {
  1561. // map pid to stream type for metadata streams
  1562. self.programMapTable['timed-metadata'][pid] = streamType;
  1563. }
  1564. // move to the next table entry
  1565. // skip past the elementary stream descriptors, if present
  1566. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  1567. }
  1568. // record the map on the packet as well
  1569. pmt.programMapTable = self.programMapTable;
  1570. };
  1571. /**
  1572. * Deliver a new MP2T packet to the stream.
  1573. */
  1574. this.push = function(packet) {
  1575. var
  1576. result = {},
  1577. offset = 4;
  1578. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  1579. // pid is a 13-bit field starting at the last bit of packet[1]
  1580. result.pid = packet[1] & 0x1f;
  1581. result.pid <<= 8;
  1582. result.pid |= packet[2];
  1583. // if an adaption field is present, its length is specified by the
  1584. // fifth byte of the TS packet header. The adaptation field is
  1585. // used to add stuffing to PES packets that don't fill a complete
  1586. // TS packet, and to specify some forms of timing and control data
  1587. // that we do not currently use.
  1588. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  1589. offset += packet[offset] + 1;
  1590. }
  1591. // parse the rest of the packet based on the type
  1592. if (result.pid === 0) {
  1593. result.type = 'pat';
  1594. parsePsi(packet.subarray(offset), result);
  1595. this.trigger('data', result);
  1596. } else if (result.pid === this.pmtPid) {
  1597. result.type = 'pmt';
  1598. parsePsi(packet.subarray(offset), result);
  1599. this.trigger('data', result);
  1600. // if there are any packets waiting for a PMT to be found, process them now
  1601. while (this.packetsWaitingForPmt.length) {
  1602. this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
  1603. }
  1604. } else if (this.programMapTable === undefined) {
  1605. // When we have not seen a PMT yet, defer further processing of
  1606. // PES packets until one has been parsed
  1607. this.packetsWaitingForPmt.push([packet, offset, result]);
  1608. } else {
  1609. this.processPes_(packet, offset, result);
  1610. }
  1611. };
  1612. this.processPes_ = function(packet, offset, result) {
  1613. // set the appropriate stream type
  1614. if (result.pid === this.programMapTable.video) {
  1615. result.streamType = StreamTypes.H264_STREAM_TYPE;
  1616. } else if (result.pid === this.programMapTable.audio) {
  1617. result.streamType = StreamTypes.ADTS_STREAM_TYPE;
  1618. } else {
  1619. // if not video or audio, it is timed-metadata or unknown
  1620. // if unknown, streamType will be undefined
  1621. result.streamType = this.programMapTable['timed-metadata'][result.pid];
  1622. }
  1623. result.type = 'pes';
  1624. result.data = packet.subarray(offset);
  1625. this.trigger('data', result);
  1626. };
  1627. };
  1628. TransportParseStream.prototype = new Stream();
  1629. TransportParseStream.STREAM_TYPES = {
  1630. h264: 0x1b,
  1631. adts: 0x0f
  1632. };
  1633. /**
  1634. * Reconsistutes program elementary stream (PES) packets from parsed
  1635. * transport stream packets. That is, if you pipe an
  1636. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  1637. * events will be events which capture the bytes for individual PES
  1638. * packets plus relevant metadata that has been extracted from the
  1639. * container.
  1640. */
  1641. ElementaryStream = function() {
  1642. var
  1643. self = this,
  1644. // PES packet fragments
  1645. video = {
  1646. data: [],
  1647. size: 0
  1648. },
  1649. audio = {
  1650. data: [],
  1651. size: 0
  1652. },
  1653. timedMetadata = {
  1654. data: [],
  1655. size: 0
  1656. },
  1657. parsePes = function(payload, pes) {
  1658. var ptsDtsFlags;
  1659. // get the packet length, this will be 0 for video
  1660. pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
  1661. // find out if this packets starts a new keyframe
  1662. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  1663. // PES packets may be annotated with a PTS value, or a PTS value
  1664. // and a DTS value. Determine what combination of values is
  1665. // available to work with.
  1666. ptsDtsFlags = payload[7];
  1667. // PTS and DTS are normally stored as a 33-bit number. Javascript
  1668. // performs all bitwise operations on 32-bit integers but javascript
  1669. // supports a much greater range (52-bits) of integer using standard
  1670. // mathematical operations.
  1671. // We construct a 31-bit value using bitwise operators over the 31
  1672. // most significant bits and then multiply by 4 (equal to a left-shift
  1673. // of 2) before we add the final 2 least significant bits of the
  1674. // timestamp (equal to an OR.)
  1675. if (ptsDtsFlags & 0xC0) {
  1676. // the PTS and DTS are not written out directly. For information
  1677. // on how they are encoded, see
  1678. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  1679. pes.pts = (payload[9] & 0x0E) << 27 |
  1680. (payload[10] & 0xFF) << 20 |
  1681. (payload[11] & 0xFE) << 12 |
  1682. (payload[12] & 0xFF) << 5 |
  1683. (payload[13] & 0xFE) >>> 3;
  1684. pes.pts *= 4; // Left shift by 2
  1685. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  1686. pes.dts = pes.pts;
  1687. if (ptsDtsFlags & 0x40) {
  1688. pes.dts = (payload[14] & 0x0E) << 27 |
  1689. (payload[15] & 0xFF) << 20 |
  1690. (payload[16] & 0xFE) << 12 |
  1691. (payload[17] & 0xFF) << 5 |
  1692. (payload[18] & 0xFE) >>> 3;
  1693. pes.dts *= 4; // Left shift by 2
  1694. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  1695. }
  1696. }
  1697. // the data section starts immediately after the PES header.
  1698. // pes_header_data_length specifies the number of header bytes
  1699. // that follow the last byte of the field.
  1700. pes.data = payload.subarray(9 + payload[8]);
  1701. },
  1702. flushStream = function(stream, type, forceFlush) {
  1703. var
  1704. packetData = new Uint8Array(stream.size),
  1705. event = {
  1706. type: type
  1707. },
  1708. i = 0,
  1709. offset = 0,
  1710. packetFlushable = false,
  1711. fragment;
  1712. // do nothing if there is not enough buffered data for a complete
  1713. // PES header
  1714. if (!stream.data.length || stream.size < 9) {
  1715. return;
  1716. }
  1717. event.trackId = stream.data[0].pid;
  1718. // reassemble the packet
  1719. for (i = 0; i < stream.data.length; i++) {
  1720. fragment = stream.data[i];
  1721. packetData.set(fragment.data, offset);
  1722. offset += fragment.data.byteLength;
  1723. }
  1724. // parse assembled packet's PES header
  1725. parsePes(packetData, event);
  1726. // non-video PES packets MUST have a non-zero PES_packet_length
  1727. // check that there is enough stream data to fill the packet
  1728. packetFlushable = type === 'video' || event.packetLength <= stream.size;
  1729. // flush pending packets if the conditions are right
  1730. if (forceFlush || packetFlushable) {
  1731. stream.size = 0;
  1732. stream.data.length = 0;
  1733. }
  1734. // only emit packets that are complete. this is to avoid assembling
  1735. // incomplete PES packets due to poor segmentation
  1736. if (packetFlushable) {
  1737. self.trigger('data', event);
  1738. }
  1739. };
  1740. ElementaryStream.prototype.init.call(this);
  1741. this.push = function(data) {
  1742. ({
  1743. pat: function() {
  1744. // we have to wait for the PMT to arrive as well before we
  1745. // have any meaningful metadata
  1746. },
  1747. pes: function() {
  1748. var stream, streamType;
  1749. switch (data.streamType) {
  1750. case StreamTypes.H264_STREAM_TYPE:
  1751. case m2tsStreamTypes.H264_STREAM_TYPE:
  1752. stream = video;
  1753. streamType = 'video';
  1754. break;
  1755. case StreamTypes.ADTS_STREAM_TYPE:
  1756. stream = audio;
  1757. streamType = 'audio';
  1758. break;
  1759. case StreamTypes.METADATA_STREAM_TYPE:
  1760. stream = timedMetadata;
  1761. streamType = 'timed-metadata';
  1762. break;
  1763. default:
  1764. // ignore unknown stream types
  1765. return;
  1766. }
  1767. // if a new packet is starting, we can flush the completed
  1768. // packet
  1769. if (data.payloadUnitStartIndicator) {
  1770. flushStream(stream, streamType, true);
  1771. }
  1772. // buffer this fragment until we are sure we've received the
  1773. // complete payload
  1774. stream.data.push(data);
  1775. stream.size += data.data.byteLength;
  1776. },
  1777. pmt: function() {
  1778. var
  1779. event = {
  1780. type: 'metadata',
  1781. tracks: []
  1782. },
  1783. programMapTable = data.programMapTable;
  1784. // translate audio and video streams to tracks
  1785. if (programMapTable.video !== null) {
  1786. event.tracks.push({
  1787. timelineStartInfo: {
  1788. baseMediaDecodeTime: 0
  1789. },
  1790. id: +programMapTable.video,
  1791. codec: 'avc',
  1792. type: 'video'
  1793. });
  1794. }
  1795. if (programMapTable.audio !== null) {
  1796. event.tracks.push({
  1797. timelineStartInfo: {
  1798. baseMediaDecodeTime: 0
  1799. },
  1800. id: +programMapTable.audio,
  1801. codec: 'adts',
  1802. type: 'audio'
  1803. });
  1804. }
  1805. self.trigger('data', event);
  1806. }
  1807. })[data.type]();
  1808. };
  1809. /**
  1810. * Flush any remaining input. Video PES packets may be of variable
  1811. * length. Normally, the start of a new video packet can trigger the
  1812. * finalization of the previous packet. That is not possible if no
  1813. * more video is forthcoming, however. In that case, some other
  1814. * mechanism (like the end of the file) has to be employed. When it is
  1815. * clear that no additional data is forthcoming, calling this method
  1816. * will flush the buffered packets.
  1817. */
  1818. this.flush = function() {
  1819. // !!THIS ORDER IS IMPORTANT!!
  1820. // video first then audio
  1821. flushStream(video, 'video');
  1822. flushStream(audio, 'audio');
  1823. flushStream(timedMetadata, 'timed-metadata');
  1824. this.trigger('done');
  1825. };
  1826. };
  1827. ElementaryStream.prototype = new Stream();
  1828. var m2ts = {
  1829. PAT_PID: 0x0000,
  1830. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  1831. TransportPacketStream: TransportPacketStream,
  1832. TransportParseStream: TransportParseStream,
  1833. ElementaryStream: ElementaryStream,
  1834. TimestampRolloverStream: TimestampRolloverStream,
  1835. CaptionStream: CaptionStream.CaptionStream,
  1836. Cea608Stream: CaptionStream.Cea608Stream,
  1837. MetadataStream: require('./metadata-stream')
  1838. };
  1839. for (var type in StreamTypes) {
  1840. if (StreamTypes.hasOwnProperty(type)) {
  1841. m2ts[type] = StreamTypes[type];
  1842. }
  1843. }
  1844. module.exports = m2ts;
  1845. },{"../utils/stream.js":15,"./caption-stream":5,"./metadata-stream":7,"./stream-types":8,"./stream-types.js":8,"./timestamp-rollover-stream":9}],7:[function(require,module,exports){
  1846. /**
  1847. * Accepts program elementary stream (PES) data events and parses out
  1848. * ID3 metadata from them, if present.
  1849. * @see http://id3.org/id3v2.3.0
  1850. */
  1851. 'use strict';
  1852. var
  1853. Stream = require('../utils/stream'),
  1854. StreamTypes = require('./stream-types'),
  1855. // return a percent-encoded representation of the specified byte range
  1856. // @see http://en.wikipedia.org/wiki/Percent-encoding
  1857. percentEncode = function(bytes, start, end) {
  1858. var i, result = '';
  1859. for (i = start; i < end; i++) {
  1860. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  1861. }
  1862. return result;
  1863. },
  1864. // return the string representation of the specified byte range,
  1865. // interpreted as UTf-8.
  1866. parseUtf8 = function(bytes, start, end) {
  1867. return decodeURIComponent(percentEncode(bytes, start, end));
  1868. },
  1869. // return the string representation of the specified byte range,
  1870. // interpreted as ISO-8859-1.
  1871. parseIso88591 = function(bytes, start, end) {
  1872. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  1873. },
  1874. parseSyncSafeInteger = function(data) {
  1875. return (data[0] << 21) |
  1876. (data[1] << 14) |
  1877. (data[2] << 7) |
  1878. (data[3]);
  1879. },
  1880. tagParsers = {
  1881. TXXX: function(tag) {
  1882. var i;
  1883. if (tag.data[0] !== 3) {
  1884. // ignore frames with unrecognized character encodings
  1885. return;
  1886. }
  1887. for (i = 1; i < tag.data.length; i++) {
  1888. if (tag.data[i] === 0) {
  1889. // parse the text fields
  1890. tag.description = parseUtf8(tag.data, 1, i);
  1891. // do not include the null terminator in the tag value
  1892. tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
  1893. break;
  1894. }
  1895. }
  1896. tag.data = tag.value;
  1897. },
  1898. WXXX: function(tag) {
  1899. var i;
  1900. if (tag.data[0] !== 3) {
  1901. // ignore frames with unrecognized character encodings
  1902. return;
  1903. }
  1904. for (i = 1; i < tag.data.length; i++) {
  1905. if (tag.data[i] === 0) {
  1906. // parse the description and URL fields
  1907. tag.description = parseUtf8(tag.data, 1, i);
  1908. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  1909. break;
  1910. }
  1911. }
  1912. },
  1913. PRIV: function(tag) {
  1914. var i;
  1915. for (i = 0; i < tag.data.length; i++) {
  1916. if (tag.data[i] === 0) {
  1917. // parse the description and URL fields
  1918. tag.owner = parseIso88591(tag.data, 0, i);
  1919. break;
  1920. }
  1921. }
  1922. tag.privateData = tag.data.subarray(i + 1);
  1923. tag.data = tag.privateData;
  1924. }
  1925. },
  1926. MetadataStream;
  1927. MetadataStream = function(options) {
  1928. var
  1929. settings = {
  1930. debug: !!(options && options.debug),
  1931. // the bytes of the program-level descriptor field in MP2T
  1932. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  1933. // program element descriptors"
  1934. descriptor: options && options.descriptor
  1935. },
  1936. // the total size in bytes of the ID3 tag being parsed
  1937. tagSize = 0,
  1938. // tag data that is not complete enough to be parsed
  1939. buffer = [],
  1940. // the total number of bytes currently in the buffer
  1941. bufferSize = 0,
  1942. i;
  1943. MetadataStream.prototype.init.call(this);
  1944. // calculate the text track in-band metadata track dispatch type
  1945. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  1946. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  1947. if (settings.descriptor) {
  1948. for (i = 0; i < settings.descriptor.length; i++) {
  1949. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  1950. }
  1951. }
  1952. this.push = function(chunk) {
  1953. var tag, frameStart, frameSize, frame, i, frameHeader;
  1954. if (chunk.type !== 'timed-metadata') {
  1955. return;
  1956. }
  1957. // if data_alignment_indicator is set in the PES header,
  1958. // we must have the start of a new ID3 tag. Assume anything
  1959. // remaining in the buffer was malformed and throw it out
  1960. if (chunk.dataAlignmentIndicator) {
  1961. bufferSize = 0;
  1962. buffer.length = 0;
  1963. }
  1964. // ignore events that don't look like ID3 data
  1965. if (buffer.length === 0 &&
  1966. (chunk.data.length < 10 ||
  1967. chunk.data[0] !== 'I'.charCodeAt(0) ||
  1968. chunk.data[1] !== 'D'.charCodeAt(0) ||
  1969. chunk.data[2] !== '3'.charCodeAt(0))) {
  1970. if (settings.debug) {
  1971. // eslint-disable-next-line no-console
  1972. console.log('Skipping unrecognized metadata packet');
  1973. }
  1974. return;
  1975. }
  1976. // add this chunk to the data we've collected so far
  1977. buffer.push(chunk);
  1978. bufferSize += chunk.data.byteLength;
  1979. // grab the size of the entire frame from the ID3 header
  1980. if (buffer.length === 1) {
  1981. // the frame size is transmitted as a 28-bit integer in the
  1982. // last four bytes of the ID3 header.
  1983. // The most significant bit of each byte is dropped and the
  1984. // results concatenated to recover the actual value.
  1985. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  1986. // ID3 reports the tag size excluding the header but it's more
  1987. // convenient for our comparisons to include it
  1988. tagSize += 10;
  1989. }
  1990. // if the entire frame has not arrived, wait for more data
  1991. if (bufferSize < tagSize) {
  1992. return;
  1993. }
  1994. // collect the entire frame so it can be parsed
  1995. tag = {
  1996. data: new Uint8Array(tagSize),
  1997. frames: [],
  1998. pts: buffer[0].pts,
  1999. dts: buffer[0].dts
  2000. };
  2001. for (i = 0; i < tagSize;) {
  2002. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  2003. i += buffer[0].data.byteLength;
  2004. bufferSize -= buffer[0].data.byteLength;
  2005. buffer.shift();
  2006. }
  2007. // find the start of the first frame and the end of the tag
  2008. frameStart = 10;
  2009. if (tag.data[5] & 0x40) {
  2010. // advance the frame start past the extended header
  2011. frameStart += 4; // header size field
  2012. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  2013. // clip any padding off the end
  2014. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  2015. }
  2016. // parse one or more ID3 frames
  2017. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  2018. do {
  2019. // determine the number of bytes in this frame
  2020. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  2021. if (frameSize < 1) {
  2022. // eslint-disable-next-line no-console
  2023. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  2024. }
  2025. frameHeader = String.fromCharCode(tag.data[frameStart],
  2026. tag.data[frameStart + 1],
  2027. tag.data[frameStart + 2],
  2028. tag.data[frameStart + 3]);
  2029. frame = {
  2030. id: frameHeader,
  2031. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  2032. };
  2033. frame.key = frame.id;
  2034. if (tagParsers[frame.id]) {
  2035. tagParsers[frame.id](frame);
  2036. // handle the special PRIV frame used to indicate the start
  2037. // time for raw AAC data
  2038. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  2039. var
  2040. d = frame.data,
  2041. size = ((d[3] & 0x01) << 30) |
  2042. (d[4] << 22) |
  2043. (d[5] << 14) |
  2044. (d[6] << 6) |
  2045. (d[7] >>> 2);
  2046. size *= 4;
  2047. size += d[7] & 0x03;
  2048. frame.timeStamp = size;
  2049. // in raw AAC, all subsequent data will be timestamped based
  2050. // on the value of this frame
  2051. // we couldn't have known the appropriate pts and dts before
  2052. // parsing this ID3 tag so set those values now
  2053. if (tag.pts === undefined && tag.dts === undefined) {
  2054. tag.pts = frame.timeStamp;
  2055. tag.dts = frame.timeStamp;
  2056. }
  2057. this.trigger('timestamp', frame);
  2058. }
  2059. }
  2060. tag.frames.push(frame);
  2061. frameStart += 10; // advance past the frame header
  2062. frameStart += frameSize; // advance past the frame body
  2063. } while (frameStart < tagSize);
  2064. this.trigger('data', tag);
  2065. };
  2066. };
  2067. MetadataStream.prototype = new Stream();
  2068. module.exports = MetadataStream;
  2069. },{"../utils/stream":15,"./stream-types":8}],8:[function(require,module,exports){
  2070. 'use strict';
  2071. module.exports = {
  2072. H264_STREAM_TYPE: 0x1B,
  2073. ADTS_STREAM_TYPE: 0x0F,
  2074. METADATA_STREAM_TYPE: 0x15
  2075. };
  2076. },{}],9:[function(require,module,exports){
  2077. /**
  2078. * mux.js
  2079. *
  2080. * Copyright (c) 2016 Brightcove
  2081. * All rights reserved.
  2082. *
  2083. * Accepts program elementary stream (PES) data events and corrects
  2084. * decode and presentation time stamps to account for a rollover
  2085. * of the 33 bit value.
  2086. */
  2087. 'use strict';
  2088. var Stream = require('../utils/stream');
  2089. var MAX_TS = 8589934592;
  2090. var RO_THRESH = 4294967296;
  2091. var handleRollover = function(value, reference) {
  2092. var direction = 1;
  2093. if (value > reference) {
  2094. // If the current timestamp value is greater than our reference timestamp and we detect a
  2095. // timestamp rollover, this means the roll over is happening in the opposite direction.
  2096. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  2097. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  2098. // rollover point. In loading this segment, the timestamp values will be very large,
  2099. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  2100. // the time stamp to be `value - 2^33`.
  2101. direction = -1;
  2102. }
  2103. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  2104. // cause an incorrect adjustment.
  2105. while (Math.abs(reference - value) > RO_THRESH) {
  2106. value += (direction * MAX_TS);
  2107. }
  2108. return value;
  2109. };
  2110. var TimestampRolloverStream = function(type) {
  2111. var lastDTS, referenceDTS;
  2112. TimestampRolloverStream.prototype.init.call(this);
  2113. this.type_ = type;
  2114. this.push = function(data) {
  2115. if (data.type !== this.type_) {
  2116. return;
  2117. }
  2118. if (referenceDTS === undefined) {
  2119. referenceDTS = data.dts;
  2120. }
  2121. data.dts = handleRollover(data.dts, referenceDTS);
  2122. data.pts = handleRollover(data.pts, referenceDTS);
  2123. lastDTS = data.dts;
  2124. this.trigger('data', data);
  2125. };
  2126. this.flush = function() {
  2127. referenceDTS = lastDTS;
  2128. this.trigger('done');
  2129. };
  2130. this.discontinuity = function() {
  2131. referenceDTS = void 0;
  2132. lastDTS = void 0;
  2133. };
  2134. };
  2135. TimestampRolloverStream.prototype = new Stream();
  2136. module.exports = {
  2137. TimestampRolloverStream: TimestampRolloverStream,
  2138. handleRollover: handleRollover
  2139. };
  2140. },{"../utils/stream":15}],10:[function(require,module,exports){
  2141. module.exports = {
  2142. generator: require('./mp4-generator'),
  2143. Transmuxer: require('./transmuxer').Transmuxer,
  2144. AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
  2145. VideoSegmentStream: require('./transmuxer').VideoSegmentStream
  2146. };
  2147. },{"./mp4-generator":11,"./transmuxer":12}],11:[function(require,module,exports){
  2148. /**
  2149. * mux.js
  2150. *
  2151. * Copyright (c) 2015 Brightcove
  2152. * All rights reserved.
  2153. *
  2154. * Functions that generate fragmented MP4s suitable for use with Media
  2155. * Source Extensions.
  2156. */
  2157. 'use strict';
  2158. var UINT32_MAX = Math.pow(2, 32) - 1;
  2159. var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
  2160. trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
  2161. trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
  2162. AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
  2163. // pre-calculate constants
  2164. (function() {
  2165. var i;
  2166. types = {
  2167. avc1: [], // codingname
  2168. avcC: [],
  2169. btrt: [],
  2170. dinf: [],
  2171. dref: [],
  2172. esds: [],
  2173. ftyp: [],
  2174. hdlr: [],
  2175. mdat: [],
  2176. mdhd: [],
  2177. mdia: [],
  2178. mfhd: [],
  2179. minf: [],
  2180. moof: [],
  2181. moov: [],
  2182. mp4a: [], // codingname
  2183. mvex: [],
  2184. mvhd: [],
  2185. sdtp: [],
  2186. smhd: [],
  2187. stbl: [],
  2188. stco: [],
  2189. stsc: [],
  2190. stsd: [],
  2191. stsz: [],
  2192. stts: [],
  2193. styp: [],
  2194. tfdt: [],
  2195. tfhd: [],
  2196. traf: [],
  2197. trak: [],
  2198. trun: [],
  2199. trex: [],
  2200. tkhd: [],
  2201. vmhd: []
  2202. };
  2203. // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  2204. // don't throw an error
  2205. if (typeof Uint8Array === 'undefined') {
  2206. return;
  2207. }
  2208. for (i in types) {
  2209. if (types.hasOwnProperty(i)) {
  2210. types[i] = [
  2211. i.charCodeAt(0),
  2212. i.charCodeAt(1),
  2213. i.charCodeAt(2),
  2214. i.charCodeAt(3)
  2215. ];
  2216. }
  2217. }
  2218. MAJOR_BRAND = new Uint8Array([
  2219. 'i'.charCodeAt(0),
  2220. 's'.charCodeAt(0),
  2221. 'o'.charCodeAt(0),
  2222. 'm'.charCodeAt(0)
  2223. ]);
  2224. AVC1_BRAND = new Uint8Array([
  2225. 'a'.charCodeAt(0),
  2226. 'v'.charCodeAt(0),
  2227. 'c'.charCodeAt(0),
  2228. '1'.charCodeAt(0)
  2229. ]);
  2230. MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  2231. VIDEO_HDLR = new Uint8Array([
  2232. 0x00, // version 0
  2233. 0x00, 0x00, 0x00, // flags
  2234. 0x00, 0x00, 0x00, 0x00, // pre_defined
  2235. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  2236. 0x00, 0x00, 0x00, 0x00, // reserved
  2237. 0x00, 0x00, 0x00, 0x00, // reserved
  2238. 0x00, 0x00, 0x00, 0x00, // reserved
  2239. 0x56, 0x69, 0x64, 0x65,
  2240. 0x6f, 0x48, 0x61, 0x6e,
  2241. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  2242. ]);
  2243. AUDIO_HDLR = new Uint8Array([
  2244. 0x00, // version 0
  2245. 0x00, 0x00, 0x00, // flags
  2246. 0x00, 0x00, 0x00, 0x00, // pre_defined
  2247. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  2248. 0x00, 0x00, 0x00, 0x00, // reserved
  2249. 0x00, 0x00, 0x00, 0x00, // reserved
  2250. 0x00, 0x00, 0x00, 0x00, // reserved
  2251. 0x53, 0x6f, 0x75, 0x6e,
  2252. 0x64, 0x48, 0x61, 0x6e,
  2253. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  2254. ]);
  2255. HDLR_TYPES = {
  2256. video: VIDEO_HDLR,
  2257. audio: AUDIO_HDLR
  2258. };
  2259. DREF = new Uint8Array([
  2260. 0x00, // version 0
  2261. 0x00, 0x00, 0x00, // flags
  2262. 0x00, 0x00, 0x00, 0x01, // entry_count
  2263. 0x00, 0x00, 0x00, 0x0c, // entry_size
  2264. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  2265. 0x00, // version 0
  2266. 0x00, 0x00, 0x01 // entry_flags
  2267. ]);
  2268. SMHD = new Uint8Array([
  2269. 0x00, // version
  2270. 0x00, 0x00, 0x00, // flags
  2271. 0x00, 0x00, // balance, 0 means centered
  2272. 0x00, 0x00 // reserved
  2273. ]);
  2274. STCO = new Uint8Array([
  2275. 0x00, // version
  2276. 0x00, 0x00, 0x00, // flags
  2277. 0x00, 0x00, 0x00, 0x00 // entry_count
  2278. ]);
  2279. STSC = STCO;
  2280. STSZ = new Uint8Array([
  2281. 0x00, // version
  2282. 0x00, 0x00, 0x00, // flags
  2283. 0x00, 0x00, 0x00, 0x00, // sample_size
  2284. 0x00, 0x00, 0x00, 0x00 // sample_count
  2285. ]);
  2286. STTS = STCO;
  2287. VMHD = new Uint8Array([
  2288. 0x00, // version
  2289. 0x00, 0x00, 0x01, // flags
  2290. 0x00, 0x00, // graphicsmode
  2291. 0x00, 0x00,
  2292. 0x00, 0x00,
  2293. 0x00, 0x00 // opcolor
  2294. ]);
  2295. }());
  2296. box = function(type) {
  2297. var
  2298. payload = [],
  2299. size = 0,
  2300. i,
  2301. result,
  2302. view;
  2303. for (i = 1; i < arguments.length; i++) {
  2304. payload.push(arguments[i]);
  2305. }
  2306. i = payload.length;
  2307. // calculate the total size we need to allocate
  2308. while (i--) {
  2309. size += payload[i].byteLength;
  2310. }
  2311. result = new Uint8Array(size + 8);
  2312. view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  2313. view.setUint32(0, result.byteLength);
  2314. result.set(type, 4);
  2315. // copy the payload into the result
  2316. for (i = 0, size = 8; i < payload.length; i++) {
  2317. result.set(payload[i], size);
  2318. size += payload[i].byteLength;
  2319. }
  2320. return result;
  2321. };
  2322. dinf = function() {
  2323. return box(types.dinf, box(types.dref, DREF));
  2324. };
  2325. esds = function(track) {
  2326. return box(types.esds, new Uint8Array([
  2327. 0x00, // version
  2328. 0x00, 0x00, 0x00, // flags
  2329. // ES_Descriptor
  2330. 0x03, // tag, ES_DescrTag
  2331. 0x19, // length
  2332. 0x00, 0x00, // ES_ID
  2333. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  2334. // DecoderConfigDescriptor
  2335. 0x04, // tag, DecoderConfigDescrTag
  2336. 0x11, // length
  2337. 0x40, // object type
  2338. 0x15, // streamType
  2339. 0x00, 0x06, 0x00, // bufferSizeDB
  2340. 0x00, 0x00, 0xda, 0xc0, // maxBitrate
  2341. 0x00, 0x00, 0xda, 0xc0, // avgBitrate
  2342. // DecoderSpecificInfo
  2343. 0x05, // tag, DecoderSpecificInfoTag
  2344. 0x02, // length
  2345. // ISO/IEC 14496-3, AudioSpecificConfig
  2346. // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  2347. (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
  2348. (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
  2349. 0x06, 0x01, 0x02 // GASpecificConfig
  2350. ]));
  2351. };
  2352. ftyp = function() {
  2353. return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
  2354. };
  2355. hdlr = function(type) {
  2356. return box(types.hdlr, HDLR_TYPES[type]);
  2357. };
  2358. mdat = function(data) {
  2359. return box(types.mdat, data);
  2360. };
  2361. mdhd = function(track) {
  2362. var result = new Uint8Array([
  2363. 0x00, // version 0
  2364. 0x00, 0x00, 0x00, // flags
  2365. 0x00, 0x00, 0x00, 0x02, // creation_time
  2366. 0x00, 0x00, 0x00, 0x03, // modification_time
  2367. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  2368. (track.duration >>> 24) & 0xFF,
  2369. (track.duration >>> 16) & 0xFF,
  2370. (track.duration >>> 8) & 0xFF,
  2371. track.duration & 0xFF, // duration
  2372. 0x55, 0xc4, // 'und' language (undetermined)
  2373. 0x00, 0x00
  2374. ]);
  2375. // Use the sample rate from the track metadata, when it is
  2376. // defined. The sample rate can be parsed out of an ADTS header, for
  2377. // instance.
  2378. if (track.samplerate) {
  2379. result[12] = (track.samplerate >>> 24) & 0xFF;
  2380. result[13] = (track.samplerate >>> 16) & 0xFF;
  2381. result[14] = (track.samplerate >>> 8) & 0xFF;
  2382. result[15] = (track.samplerate) & 0xFF;
  2383. }
  2384. return box(types.mdhd, result);
  2385. };
  2386. mdia = function(track) {
  2387. return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
  2388. };
  2389. mfhd = function(sequenceNumber) {
  2390. return box(types.mfhd, new Uint8Array([
  2391. 0x00,
  2392. 0x00, 0x00, 0x00, // flags
  2393. (sequenceNumber & 0xFF000000) >> 24,
  2394. (sequenceNumber & 0xFF0000) >> 16,
  2395. (sequenceNumber & 0xFF00) >> 8,
  2396. sequenceNumber & 0xFF // sequence_number
  2397. ]));
  2398. };
  2399. minf = function(track) {
  2400. return box(types.minf,
  2401. track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
  2402. dinf(),
  2403. stbl(track));
  2404. };
  2405. moof = function(sequenceNumber, tracks) {
  2406. var
  2407. trackFragments = [],
  2408. i = tracks.length;
  2409. // build traf boxes for each track fragment
  2410. while (i--) {
  2411. trackFragments[i] = traf(tracks[i]);
  2412. }
  2413. return box.apply(null, [
  2414. types.moof,
  2415. mfhd(sequenceNumber)
  2416. ].concat(trackFragments));
  2417. };
  2418. /**
  2419. * Returns a movie box.
  2420. * @param tracks {array} the tracks associated with this movie
  2421. * @see ISO/IEC 14496-12:2012(E), section 8.2.1
  2422. */
  2423. moov = function(tracks) {
  2424. var
  2425. i = tracks.length,
  2426. boxes = [];
  2427. while (i--) {
  2428. boxes[i] = trak(tracks[i]);
  2429. }
  2430. return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
  2431. };
  2432. mvex = function(tracks) {
  2433. var
  2434. i = tracks.length,
  2435. boxes = [];
  2436. while (i--) {
  2437. boxes[i] = trex(tracks[i]);
  2438. }
  2439. return box.apply(null, [types.mvex].concat(boxes));
  2440. };
  2441. mvhd = function(duration) {
  2442. var
  2443. bytes = new Uint8Array([
  2444. 0x00, // version 0
  2445. 0x00, 0x00, 0x00, // flags
  2446. 0x00, 0x00, 0x00, 0x01, // creation_time
  2447. 0x00, 0x00, 0x00, 0x02, // modification_time
  2448. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  2449. (duration & 0xFF000000) >> 24,
  2450. (duration & 0xFF0000) >> 16,
  2451. (duration & 0xFF00) >> 8,
  2452. duration & 0xFF, // duration
  2453. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  2454. 0x01, 0x00, // 1.0 volume
  2455. 0x00, 0x00, // reserved
  2456. 0x00, 0x00, 0x00, 0x00, // reserved
  2457. 0x00, 0x00, 0x00, 0x00, // reserved
  2458. 0x00, 0x01, 0x00, 0x00,
  2459. 0x00, 0x00, 0x00, 0x00,
  2460. 0x00, 0x00, 0x00, 0x00,
  2461. 0x00, 0x00, 0x00, 0x00,
  2462. 0x00, 0x01, 0x00, 0x00,
  2463. 0x00, 0x00, 0x00, 0x00,
  2464. 0x00, 0x00, 0x00, 0x00,
  2465. 0x00, 0x00, 0x00, 0x00,
  2466. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  2467. 0x00, 0x00, 0x00, 0x00,
  2468. 0x00, 0x00, 0x00, 0x00,
  2469. 0x00, 0x00, 0x00, 0x00,
  2470. 0x00, 0x00, 0x00, 0x00,
  2471. 0x00, 0x00, 0x00, 0x00,
  2472. 0x00, 0x00, 0x00, 0x00, // pre_defined
  2473. 0xff, 0xff, 0xff, 0xff // next_track_ID
  2474. ]);
  2475. return box(types.mvhd, bytes);
  2476. };
  2477. sdtp = function(track) {
  2478. var
  2479. samples = track.samples || [],
  2480. bytes = new Uint8Array(4 + samples.length),
  2481. flags,
  2482. i;
  2483. // leave the full box header (4 bytes) all zero
  2484. // write the sample table
  2485. for (i = 0; i < samples.length; i++) {
  2486. flags = samples[i].flags;
  2487. bytes[i + 4] = (flags.dependsOn << 4) |
  2488. (flags.isDependedOn << 2) |
  2489. (flags.hasRedundancy);
  2490. }
  2491. return box(types.sdtp,
  2492. bytes);
  2493. };
  2494. stbl = function(track) {
  2495. return box(types.stbl,
  2496. stsd(track),
  2497. box(types.stts, STTS),
  2498. box(types.stsc, STSC),
  2499. box(types.stsz, STSZ),
  2500. box(types.stco, STCO));
  2501. };
  2502. (function() {
  2503. var videoSample, audioSample;
  2504. stsd = function(track) {
  2505. return box(types.stsd, new Uint8Array([
  2506. 0x00, // version 0
  2507. 0x00, 0x00, 0x00, // flags
  2508. 0x00, 0x00, 0x00, 0x01
  2509. ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  2510. };
  2511. videoSample = function(track) {
  2512. var
  2513. sps = track.sps || [],
  2514. pps = track.pps || [],
  2515. sequenceParameterSets = [],
  2516. pictureParameterSets = [],
  2517. i;
  2518. // assemble the SPSs
  2519. for (i = 0; i < sps.length; i++) {
  2520. sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
  2521. sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
  2522. sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
  2523. }
  2524. // assemble the PPSs
  2525. for (i = 0; i < pps.length; i++) {
  2526. pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
  2527. pictureParameterSets.push((pps[i].byteLength & 0xFF));
  2528. pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
  2529. }
  2530. return box(types.avc1, new Uint8Array([
  2531. 0x00, 0x00, 0x00,
  2532. 0x00, 0x00, 0x00, // reserved
  2533. 0x00, 0x01, // data_reference_index
  2534. 0x00, 0x00, // pre_defined
  2535. 0x00, 0x00, // reserved
  2536. 0x00, 0x00, 0x00, 0x00,
  2537. 0x00, 0x00, 0x00, 0x00,
  2538. 0x00, 0x00, 0x00, 0x00, // pre_defined
  2539. (track.width & 0xff00) >> 8,
  2540. track.width & 0xff, // width
  2541. (track.height & 0xff00) >> 8,
  2542. track.height & 0xff, // height
  2543. 0x00, 0x48, 0x00, 0x00, // horizresolution
  2544. 0x00, 0x48, 0x00, 0x00, // vertresolution
  2545. 0x00, 0x00, 0x00, 0x00, // reserved
  2546. 0x00, 0x01, // frame_count
  2547. 0x13,
  2548. 0x76, 0x69, 0x64, 0x65,
  2549. 0x6f, 0x6a, 0x73, 0x2d,
  2550. 0x63, 0x6f, 0x6e, 0x74,
  2551. 0x72, 0x69, 0x62, 0x2d,
  2552. 0x68, 0x6c, 0x73, 0x00,
  2553. 0x00, 0x00, 0x00, 0x00,
  2554. 0x00, 0x00, 0x00, 0x00,
  2555. 0x00, 0x00, 0x00, // compressorname
  2556. 0x00, 0x18, // depth = 24
  2557. 0x11, 0x11 // pre_defined = -1
  2558. ]), box(types.avcC, new Uint8Array([
  2559. 0x01, // configurationVersion
  2560. track.profileIdc, // AVCProfileIndication
  2561. track.profileCompatibility, // profile_compatibility
  2562. track.levelIdc, // AVCLevelIndication
  2563. 0xff // lengthSizeMinusOne, hard-coded to 4 bytes
  2564. ].concat([
  2565. sps.length // numOfSequenceParameterSets
  2566. ]).concat(sequenceParameterSets).concat([
  2567. pps.length // numOfPictureParameterSets
  2568. ]).concat(pictureParameterSets))), // "PPS"
  2569. box(types.btrt, new Uint8Array([
  2570. 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  2571. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  2572. 0x00, 0x2d, 0xc6, 0xc0
  2573. ])) // avgBitrate
  2574. );
  2575. };
  2576. audioSample = function(track) {
  2577. return box(types.mp4a, new Uint8Array([
  2578. // SampleEntry, ISO/IEC 14496-12
  2579. 0x00, 0x00, 0x00,
  2580. 0x00, 0x00, 0x00, // reserved
  2581. 0x00, 0x01, // data_reference_index
  2582. // AudioSampleEntry, ISO/IEC 14496-12
  2583. 0x00, 0x00, 0x00, 0x00, // reserved
  2584. 0x00, 0x00, 0x00, 0x00, // reserved
  2585. (track.channelcount & 0xff00) >> 8,
  2586. (track.channelcount & 0xff), // channelcount
  2587. (track.samplesize & 0xff00) >> 8,
  2588. (track.samplesize & 0xff), // samplesize
  2589. 0x00, 0x00, // pre_defined
  2590. 0x00, 0x00, // reserved
  2591. (track.samplerate & 0xff00) >> 8,
  2592. (track.samplerate & 0xff),
  2593. 0x00, 0x00 // samplerate, 16.16
  2594. // MP4AudioSampleEntry, ISO/IEC 14496-14
  2595. ]), esds(track));
  2596. };
  2597. }());
  2598. tkhd = function(track) {
  2599. var result = new Uint8Array([
  2600. 0x00, // version 0
  2601. 0x00, 0x00, 0x07, // flags
  2602. 0x00, 0x00, 0x00, 0x00, // creation_time
  2603. 0x00, 0x00, 0x00, 0x00, // modification_time
  2604. (track.id & 0xFF000000) >> 24,
  2605. (track.id & 0xFF0000) >> 16,
  2606. (track.id & 0xFF00) >> 8,
  2607. track.id & 0xFF, // track_ID
  2608. 0x00, 0x00, 0x00, 0x00, // reserved
  2609. (track.duration & 0xFF000000) >> 24,
  2610. (track.duration & 0xFF0000) >> 16,
  2611. (track.duration & 0xFF00) >> 8,
  2612. track.duration & 0xFF, // duration
  2613. 0x00, 0x00, 0x00, 0x00,
  2614. 0x00, 0x00, 0x00, 0x00, // reserved
  2615. 0x00, 0x00, // layer
  2616. 0x00, 0x00, // alternate_group
  2617. 0x01, 0x00, // non-audio track volume
  2618. 0x00, 0x00, // reserved
  2619. 0x00, 0x01, 0x00, 0x00,
  2620. 0x00, 0x00, 0x00, 0x00,
  2621. 0x00, 0x00, 0x00, 0x00,
  2622. 0x00, 0x00, 0x00, 0x00,
  2623. 0x00, 0x01, 0x00, 0x00,
  2624. 0x00, 0x00, 0x00, 0x00,
  2625. 0x00, 0x00, 0x00, 0x00,
  2626. 0x00, 0x00, 0x00, 0x00,
  2627. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  2628. (track.width & 0xFF00) >> 8,
  2629. track.width & 0xFF,
  2630. 0x00, 0x00, // width
  2631. (track.height & 0xFF00) >> 8,
  2632. track.height & 0xFF,
  2633. 0x00, 0x00 // height
  2634. ]);
  2635. return box(types.tkhd, result);
  2636. };
  2637. /**
  2638. * Generate a track fragment (traf) box. A traf box collects metadata
  2639. * about tracks in a movie fragment (moof) box.
  2640. */
  2641. traf = function(track) {
  2642. var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
  2643. sampleDependencyTable, dataOffset,
  2644. upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  2645. trackFragmentHeader = box(types.tfhd, new Uint8Array([
  2646. 0x00, // version 0
  2647. 0x00, 0x00, 0x3a, // flags
  2648. (track.id & 0xFF000000) >> 24,
  2649. (track.id & 0xFF0000) >> 16,
  2650. (track.id & 0xFF00) >> 8,
  2651. (track.id & 0xFF), // track_ID
  2652. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  2653. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  2654. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  2655. 0x00, 0x00, 0x00, 0x00 // default_sample_flags
  2656. ]));
  2657. upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  2658. lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  2659. trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
  2660. 0x01, // version 1
  2661. 0x00, 0x00, 0x00, // flags
  2662. // baseMediaDecodeTime
  2663. (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
  2664. (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
  2665. (upperWordBaseMediaDecodeTime >>> 8) & 0xFF,
  2666. upperWordBaseMediaDecodeTime & 0xFF,
  2667. (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
  2668. (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
  2669. (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF,
  2670. lowerWordBaseMediaDecodeTime & 0xFF
  2671. ]));
  2672. // the data offset specifies the number of bytes from the start of
  2673. // the containing moof to the first payload byte of the associated
  2674. // mdat
  2675. dataOffset = (32 + // tfhd
  2676. 20 + // tfdt
  2677. 8 + // traf header
  2678. 16 + // mfhd
  2679. 8 + // moof header
  2680. 8); // mdat header
  2681. // audio tracks require less metadata
  2682. if (track.type === 'audio') {
  2683. trackFragmentRun = trun(track, dataOffset);
  2684. return box(types.traf,
  2685. trackFragmentHeader,
  2686. trackFragmentDecodeTime,
  2687. trackFragmentRun);
  2688. }
  2689. // video tracks should contain an independent and disposable samples
  2690. // box (sdtp)
  2691. // generate one and adjust offsets to match
  2692. sampleDependencyTable = sdtp(track);
  2693. trackFragmentRun = trun(track,
  2694. sampleDependencyTable.length + dataOffset);
  2695. return box(types.traf,
  2696. trackFragmentHeader,
  2697. trackFragmentDecodeTime,
  2698. trackFragmentRun,
  2699. sampleDependencyTable);
  2700. };
  2701. /**
  2702. * Generate a track box.
  2703. * @param track {object} a track definition
  2704. * @return {Uint8Array} the track box
  2705. */
  2706. trak = function(track) {
  2707. track.duration = track.duration || 0xffffffff;
  2708. return box(types.trak,
  2709. tkhd(track),
  2710. mdia(track));
  2711. };
  2712. trex = function(track) {
  2713. var result = new Uint8Array([
  2714. 0x00, // version 0
  2715. 0x00, 0x00, 0x00, // flags
  2716. (track.id & 0xFF000000) >> 24,
  2717. (track.id & 0xFF0000) >> 16,
  2718. (track.id & 0xFF00) >> 8,
  2719. (track.id & 0xFF), // track_ID
  2720. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  2721. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  2722. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  2723. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  2724. ]);
  2725. // the last two bytes of default_sample_flags is the sample
  2726. // degradation priority, a hint about the importance of this sample
  2727. // relative to others. Lower the degradation priority for all sample
  2728. // types other than video.
  2729. if (track.type !== 'video') {
  2730. result[result.length - 1] = 0x00;
  2731. }
  2732. return box(types.trex, result);
  2733. };
  2734. (function() {
  2735. var audioTrun, videoTrun, trunHeader;
  2736. // This method assumes all samples are uniform. That is, if a
  2737. // duration is present for the first sample, it will be present for
  2738. // all subsequent samples.
  2739. // see ISO/IEC 14496-12:2012, Section 8.8.8.1
  2740. trunHeader = function(samples, offset) {
  2741. var durationPresent = 0, sizePresent = 0,
  2742. flagsPresent = 0, compositionTimeOffset = 0;
  2743. // trun flag constants
  2744. if (samples.length) {
  2745. if (samples[0].duration !== undefined) {
  2746. durationPresent = 0x1;
  2747. }
  2748. if (samples[0].size !== undefined) {
  2749. sizePresent = 0x2;
  2750. }
  2751. if (samples[0].flags !== undefined) {
  2752. flagsPresent = 0x4;
  2753. }
  2754. if (samples[0].compositionTimeOffset !== undefined) {
  2755. compositionTimeOffset = 0x8;
  2756. }
  2757. }
  2758. return [
  2759. 0x00, // version 0
  2760. 0x00,
  2761. durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
  2762. 0x01, // flags
  2763. (samples.length & 0xFF000000) >>> 24,
  2764. (samples.length & 0xFF0000) >>> 16,
  2765. (samples.length & 0xFF00) >>> 8,
  2766. samples.length & 0xFF, // sample_count
  2767. (offset & 0xFF000000) >>> 24,
  2768. (offset & 0xFF0000) >>> 16,
  2769. (offset & 0xFF00) >>> 8,
  2770. offset & 0xFF // data_offset
  2771. ];
  2772. };
  2773. videoTrun = function(track, offset) {
  2774. var bytes, samples, sample, i;
  2775. samples = track.samples || [];
  2776. offset += 8 + 12 + (16 * samples.length);
  2777. bytes = trunHeader(samples, offset);
  2778. for (i = 0; i < samples.length; i++) {
  2779. sample = samples[i];
  2780. bytes = bytes.concat([
  2781. (sample.duration & 0xFF000000) >>> 24,
  2782. (sample.duration & 0xFF0000) >>> 16,
  2783. (sample.duration & 0xFF00) >>> 8,
  2784. sample.duration & 0xFF, // sample_duration
  2785. (sample.size & 0xFF000000) >>> 24,
  2786. (sample.size & 0xFF0000) >>> 16,
  2787. (sample.size & 0xFF00) >>> 8,
  2788. sample.size & 0xFF, // sample_size
  2789. (sample.flags.isLeading << 2) | sample.flags.dependsOn,
  2790. (sample.flags.isDependedOn << 6) |
  2791. (sample.flags.hasRedundancy << 4) |
  2792. (sample.flags.paddingValue << 1) |
  2793. sample.flags.isNonSyncSample,
  2794. sample.flags.degradationPriority & 0xF0 << 8,
  2795. sample.flags.degradationPriority & 0x0F, // sample_flags
  2796. (sample.compositionTimeOffset & 0xFF000000) >>> 24,
  2797. (sample.compositionTimeOffset & 0xFF0000) >>> 16,
  2798. (sample.compositionTimeOffset & 0xFF00) >>> 8,
  2799. sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
  2800. ]);
  2801. }
  2802. return box(types.trun, new Uint8Array(bytes));
  2803. };
  2804. audioTrun = function(track, offset) {
  2805. var bytes, samples, sample, i;
  2806. samples = track.samples || [];
  2807. offset += 8 + 12 + (8 * samples.length);
  2808. bytes = trunHeader(samples, offset);
  2809. for (i = 0; i < samples.length; i++) {
  2810. sample = samples[i];
  2811. bytes = bytes.concat([
  2812. (sample.duration & 0xFF000000) >>> 24,
  2813. (sample.duration & 0xFF0000) >>> 16,
  2814. (sample.duration & 0xFF00) >>> 8,
  2815. sample.duration & 0xFF, // sample_duration
  2816. (sample.size & 0xFF000000) >>> 24,
  2817. (sample.size & 0xFF0000) >>> 16,
  2818. (sample.size & 0xFF00) >>> 8,
  2819. sample.size & 0xFF]); // sample_size
  2820. }
  2821. return box(types.trun, new Uint8Array(bytes));
  2822. };
  2823. trun = function(track, offset) {
  2824. if (track.type === 'audio') {
  2825. return audioTrun(track, offset);
  2826. }
  2827. return videoTrun(track, offset);
  2828. };
  2829. }());
  2830. module.exports = {
  2831. ftyp: ftyp,
  2832. mdat: mdat,
  2833. moof: moof,
  2834. moov: moov,
  2835. initSegment: function(tracks) {
  2836. var
  2837. fileType = ftyp(),
  2838. movie = moov(tracks),
  2839. result;
  2840. result = new Uint8Array(fileType.byteLength + movie.byteLength);
  2841. result.set(fileType);
  2842. result.set(movie, fileType.byteLength);
  2843. return result;
  2844. }
  2845. };
  2846. },{}],12:[function(require,module,exports){
  2847. /**
  2848. * mux.js
  2849. *
  2850. * Copyright (c) 2015 Brightcove
  2851. * All rights reserved.
  2852. *
  2853. * A stream-based mp2t to mp4 converter. This utility can be used to
  2854. * deliver mp4s to a SourceBuffer on platforms that support native
  2855. * Media Source Extensions.
  2856. */
  2857. 'use strict';
  2858. var Stream = require('../utils/stream.js');
  2859. var mp4 = require('./mp4-generator.js');
  2860. var m2ts = require('../m2ts/m2ts.js');
  2861. var AdtsStream = require('../codecs/adts.js');
  2862. var H264Stream = require('../codecs/h264').H264Stream;
  2863. var AacStream = require('../aac');
  2864. var coneOfSilence = require('../data/silence');
  2865. var clock = require('../utils/clock');
  2866. // constants
  2867. var AUDIO_PROPERTIES = [
  2868. 'audioobjecttype',
  2869. 'channelcount',
  2870. 'samplerate',
  2871. 'samplingfrequencyindex',
  2872. 'samplesize'
  2873. ];
  2874. var VIDEO_PROPERTIES = [
  2875. 'width',
  2876. 'height',
  2877. 'profileIdc',
  2878. 'levelIdc',
  2879. 'profileCompatibility'
  2880. ];
  2881. var ONE_SECOND_IN_TS = 90000; // 90kHz clock
  2882. // object types
  2883. var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
  2884. // Helper functions
  2885. var
  2886. createDefaultSample,
  2887. isLikelyAacData,
  2888. collectDtsInfo,
  2889. clearDtsInfo,
  2890. calculateTrackBaseMediaDecodeTime,
  2891. arrayEquals,
  2892. sumFrameByteLengths;
  2893. /**
  2894. * Default sample object
  2895. * see ISO/IEC 14496-12:2012, section 8.6.4.3
  2896. */
  2897. createDefaultSample = function() {
  2898. return {
  2899. size: 0,
  2900. flags: {
  2901. isLeading: 0,
  2902. dependsOn: 1,
  2903. isDependedOn: 0,
  2904. hasRedundancy: 0,
  2905. degradationPriority: 0
  2906. }
  2907. };
  2908. };
  2909. isLikelyAacData = function(data) {
  2910. if ((data[0] === 'I'.charCodeAt(0)) &&
  2911. (data[1] === 'D'.charCodeAt(0)) &&
  2912. (data[2] === '3'.charCodeAt(0))) {
  2913. return true;
  2914. }
  2915. return false;
  2916. };
  2917. /**
  2918. * Compare two arrays (even typed) for same-ness
  2919. */
  2920. arrayEquals = function(a, b) {
  2921. var
  2922. i;
  2923. if (a.length !== b.length) {
  2924. return false;
  2925. }
  2926. // compare the value of each element in the array
  2927. for (i = 0; i < a.length; i++) {
  2928. if (a[i] !== b[i]) {
  2929. return false;
  2930. }
  2931. }
  2932. return true;
  2933. };
  2934. /**
  2935. * Sum the `byteLength` properties of the data in each AAC frame
  2936. */
  2937. sumFrameByteLengths = function(array) {
  2938. var
  2939. i,
  2940. currentObj,
  2941. sum = 0;
  2942. // sum the byteLength's all each nal unit in the frame
  2943. for (i = 0; i < array.length; i++) {
  2944. currentObj = array[i];
  2945. sum += currentObj.data.byteLength;
  2946. }
  2947. return sum;
  2948. };
  2949. /**
  2950. * Constructs a single-track, ISO BMFF media segment from AAC data
  2951. * events. The output of this stream can be fed to a SourceBuffer
  2952. * configured with a suitable initialization segment.
  2953. */
  2954. AudioSegmentStream = function(track) {
  2955. var
  2956. adtsFrames = [],
  2957. sequenceNumber = 0,
  2958. earliestAllowedDts = 0,
  2959. audioAppendStartTs = 0,
  2960. videoBaseMediaDecodeTime = Infinity;
  2961. AudioSegmentStream.prototype.init.call(this);
  2962. this.push = function(data) {
  2963. collectDtsInfo(track, data);
  2964. if (track) {
  2965. AUDIO_PROPERTIES.forEach(function(prop) {
  2966. track[prop] = data[prop];
  2967. });
  2968. }
  2969. // buffer audio data until end() is called
  2970. adtsFrames.push(data);
  2971. };
  2972. this.setEarliestDts = function(earliestDts) {
  2973. earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  2974. };
  2975. this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  2976. videoBaseMediaDecodeTime = baseMediaDecodeTime;
  2977. };
  2978. this.setAudioAppendStart = function(timestamp) {
  2979. audioAppendStartTs = timestamp;
  2980. };
  2981. this.flush = function() {
  2982. var
  2983. frames,
  2984. moof,
  2985. mdat,
  2986. boxes;
  2987. // return early if no audio data has been observed
  2988. if (adtsFrames.length === 0) {
  2989. this.trigger('done', 'AudioSegmentStream');
  2990. return;
  2991. }
  2992. frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
  2993. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  2994. this.prefixWithSilence_(track, frames);
  2995. // we have to build the index from byte locations to
  2996. // samples (that is, adts frames) in the audio data
  2997. track.samples = this.generateSampleTable_(frames);
  2998. // concatenate the audio data to constuct the mdat
  2999. mdat = mp4.mdat(this.concatenateFrameData_(frames));
  3000. adtsFrames = [];
  3001. moof = mp4.moof(sequenceNumber, [track]);
  3002. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  3003. // bump the sequence number for next time
  3004. sequenceNumber++;
  3005. boxes.set(moof);
  3006. boxes.set(mdat, moof.byteLength);
  3007. clearDtsInfo(track);
  3008. this.trigger('data', {track: track, boxes: boxes});
  3009. this.trigger('done', 'AudioSegmentStream');
  3010. };
  3011. // Possibly pad (prefix) the audio track with silence if appending this track
  3012. // would lead to the introduction of a gap in the audio buffer
  3013. this.prefixWithSilence_ = function(track, frames) {
  3014. var
  3015. baseMediaDecodeTimeTs,
  3016. frameDuration = 0,
  3017. audioGapDuration = 0,
  3018. audioFillFrameCount = 0,
  3019. audioFillDuration = 0,
  3020. silentFrame,
  3021. i;
  3022. if (!frames.length) {
  3023. return;
  3024. }
  3025. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
  3026. // determine frame clock duration based on sample rate, round up to avoid overfills
  3027. frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
  3028. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  3029. // insert the shortest possible amount (audio gap or audio to video gap)
  3030. audioGapDuration =
  3031. baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
  3032. // number of full frames in the audio gap
  3033. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  3034. audioFillDuration = audioFillFrameCount * frameDuration;
  3035. }
  3036. // don't attempt to fill gaps smaller than a single frame or larger
  3037. // than a half second
  3038. if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
  3039. return;
  3040. }
  3041. silentFrame = coneOfSilence[track.samplerate];
  3042. if (!silentFrame) {
  3043. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  3044. // from the content instead
  3045. silentFrame = frames[0].data;
  3046. }
  3047. for (i = 0; i < audioFillFrameCount; i++) {
  3048. frames.splice(i, 0, {
  3049. data: silentFrame
  3050. });
  3051. }
  3052. track.baseMediaDecodeTime -=
  3053. Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  3054. };
  3055. // If the audio segment extends before the earliest allowed dts
  3056. // value, remove AAC frames until starts at or after the earliest
  3057. // allowed DTS so that we don't end up with a negative baseMedia-
  3058. // DecodeTime for the audio track
  3059. this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
  3060. if (track.minSegmentDts >= earliestAllowedDts) {
  3061. return adtsFrames;
  3062. }
  3063. // We will need to recalculate the earliest segment Dts
  3064. track.minSegmentDts = Infinity;
  3065. return adtsFrames.filter(function(currentFrame) {
  3066. // If this is an allowed frame, keep it and record it's Dts
  3067. if (currentFrame.dts >= earliestAllowedDts) {
  3068. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  3069. track.minSegmentPts = track.minSegmentDts;
  3070. return true;
  3071. }
  3072. // Otherwise, discard it
  3073. return false;
  3074. });
  3075. };
  3076. // generate the track's raw mdat data from an array of frames
  3077. this.generateSampleTable_ = function(frames) {
  3078. var
  3079. i,
  3080. currentFrame,
  3081. samples = [];
  3082. for (i = 0; i < frames.length; i++) {
  3083. currentFrame = frames[i];
  3084. samples.push({
  3085. size: currentFrame.data.byteLength,
  3086. duration: 1024 // For AAC audio, all samples contain 1024 samples
  3087. });
  3088. }
  3089. return samples;
  3090. };
  3091. // generate the track's sample table from an array of frames
  3092. this.concatenateFrameData_ = function(frames) {
  3093. var
  3094. i,
  3095. currentFrame,
  3096. dataOffset = 0,
  3097. data = new Uint8Array(sumFrameByteLengths(frames));
  3098. for (i = 0; i < frames.length; i++) {
  3099. currentFrame = frames[i];
  3100. data.set(currentFrame.data, dataOffset);
  3101. dataOffset += currentFrame.data.byteLength;
  3102. }
  3103. return data;
  3104. };
  3105. };
  3106. AudioSegmentStream.prototype = new Stream();
  3107. /**
  3108. * Constructs a single-track, ISO BMFF media segment from H264 data
  3109. * events. The output of this stream can be fed to a SourceBuffer
  3110. * configured with a suitable initialization segment.
  3111. * @param track {object} track metadata configuration
  3112. * @param options {object} transmuxer options object
  3113. * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
  3114. * gopsToAlignWith list when attempting to align gop pts
  3115. */
  3116. VideoSegmentStream = function(track, options) {
  3117. var
  3118. sequenceNumber = 0,
  3119. nalUnits = [],
  3120. gopsToAlignWith = [],
  3121. config,
  3122. pps;
  3123. options = options || {};
  3124. VideoSegmentStream.prototype.init.call(this);
  3125. delete track.minPTS;
  3126. this.gopCache_ = [];
  3127. this.push = function(nalUnit) {
  3128. collectDtsInfo(track, nalUnit);
  3129. // record the track config
  3130. if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
  3131. config = nalUnit.config;
  3132. track.sps = [nalUnit.data];
  3133. VIDEO_PROPERTIES.forEach(function(prop) {
  3134. track[prop] = config[prop];
  3135. }, this);
  3136. }
  3137. if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
  3138. !pps) {
  3139. pps = nalUnit.data;
  3140. track.pps = [nalUnit.data];
  3141. }
  3142. // buffer video until flush() is called
  3143. nalUnits.push(nalUnit);
  3144. };
  3145. this.flush = function() {
  3146. var
  3147. frames,
  3148. gopForFusion,
  3149. gops,
  3150. moof,
  3151. mdat,
  3152. boxes;
  3153. // Throw away nalUnits at the start of the byte stream until
  3154. // we find the first AUD
  3155. while (nalUnits.length) {
  3156. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  3157. break;
  3158. }
  3159. nalUnits.shift();
  3160. }
  3161. // Return early if no video data has been observed
  3162. if (nalUnits.length === 0) {
  3163. this.resetStream_();
  3164. this.trigger('done', 'VideoSegmentStream');
  3165. return;
  3166. }
  3167. // Organize the raw nal-units into arrays that represent
  3168. // higher-level constructs such as frames and gops
  3169. // (group-of-pictures)
  3170. frames = this.groupNalsIntoFrames_(nalUnits);
  3171. gops = this.groupFramesIntoGops_(frames);
  3172. // If the first frame of this fragment is not a keyframe we have
  3173. // a problem since MSE (on Chrome) requires a leading keyframe.
  3174. //
  3175. // We have two approaches to repairing this situation:
  3176. // 1) GOP-FUSION:
  3177. // This is where we keep track of the GOPS (group-of-pictures)
  3178. // from previous fragments and attempt to find one that we can
  3179. // prepend to the current fragment in order to create a valid
  3180. // fragment.
  3181. // 2) KEYFRAME-PULLING:
  3182. // Here we search for the first keyframe in the fragment and
  3183. // throw away all the frames between the start of the fragment
  3184. // and that keyframe. We then extend the duration and pull the
  3185. // PTS of the keyframe forward so that it covers the time range
  3186. // of the frames that were disposed of.
  3187. //
  3188. // #1 is far prefereable over #2 which can cause "stuttering" but
  3189. // requires more things to be just right.
  3190. if (!gops[0][0].keyFrame) {
  3191. // Search for a gop for fusion from our gopCache
  3192. gopForFusion = this.getGopForFusion_(nalUnits[0], track);
  3193. if (gopForFusion) {
  3194. gops.unshift(gopForFusion);
  3195. // Adjust Gops' metadata to account for the inclusion of the
  3196. // new gop at the beginning
  3197. gops.byteLength += gopForFusion.byteLength;
  3198. gops.nalCount += gopForFusion.nalCount;
  3199. gops.pts = gopForFusion.pts;
  3200. gops.dts = gopForFusion.dts;
  3201. gops.duration += gopForFusion.duration;
  3202. } else {
  3203. // If we didn't find a candidate gop fall back to keyrame-pulling
  3204. gops = this.extendFirstKeyFrame_(gops);
  3205. }
  3206. }
  3207. // Trim gops to align with gopsToAlignWith
  3208. if (gopsToAlignWith.length) {
  3209. var alignedGops;
  3210. if (options.alignGopsAtEnd) {
  3211. alignedGops = this.alignGopsAtEnd_(gops);
  3212. } else {
  3213. alignedGops = this.alignGopsAtStart_(gops);
  3214. }
  3215. if (!alignedGops) {
  3216. // save all the nals in the last GOP into the gop cache
  3217. this.gopCache_.unshift({
  3218. gop: gops.pop(),
  3219. pps: track.pps,
  3220. sps: track.sps
  3221. });
  3222. // Keep a maximum of 6 GOPs in the cache
  3223. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  3224. // Clear nalUnits
  3225. nalUnits = [];
  3226. // return early no gops can be aligned with desired gopsToAlignWith
  3227. this.resetStream_();
  3228. this.trigger('done', 'VideoSegmentStream');
  3229. return;
  3230. }
  3231. // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
  3232. // when recalculated before sending off to CoalesceStream
  3233. clearDtsInfo(track);
  3234. gops = alignedGops;
  3235. }
  3236. collectDtsInfo(track, gops);
  3237. // First, we have to build the index from byte locations to
  3238. // samples (that is, frames) in the video data
  3239. track.samples = this.generateSampleTable_(gops);
  3240. // Concatenate the video data and construct the mdat
  3241. mdat = mp4.mdat(this.concatenateNalData_(gops));
  3242. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  3243. this.trigger('processedGopsInfo', gops.map(function(gop) {
  3244. return {
  3245. pts: gop.pts,
  3246. dts: gop.dts,
  3247. byteLength: gop.byteLength
  3248. };
  3249. }));
  3250. // save all the nals in the last GOP into the gop cache
  3251. this.gopCache_.unshift({
  3252. gop: gops.pop(),
  3253. pps: track.pps,
  3254. sps: track.sps
  3255. });
  3256. // Keep a maximum of 6 GOPs in the cache
  3257. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  3258. // Clear nalUnits
  3259. nalUnits = [];
  3260. this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
  3261. this.trigger('timelineStartInfo', track.timelineStartInfo);
  3262. moof = mp4.moof(sequenceNumber, [track]);
  3263. // it would be great to allocate this array up front instead of
  3264. // throwing away hundreds of media segment fragments
  3265. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  3266. // Bump the sequence number for next time
  3267. sequenceNumber++;
  3268. boxes.set(moof);
  3269. boxes.set(mdat, moof.byteLength);
  3270. this.trigger('data', {track: track, boxes: boxes});
  3271. this.resetStream_();
  3272. // Continue with the flush process now
  3273. this.trigger('done', 'VideoSegmentStream');
  3274. };
  3275. this.resetStream_ = function() {
  3276. clearDtsInfo(track);
  3277. // reset config and pps because they may differ across segments
  3278. // for instance, when we are rendition switching
  3279. config = undefined;
  3280. pps = undefined;
  3281. };
  3282. // Search for a candidate Gop for gop-fusion from the gop cache and
  3283. // return it or return null if no good candidate was found
  3284. this.getGopForFusion_ = function(nalUnit) {
  3285. var
  3286. halfSecond = 45000, // Half-a-second in a 90khz clock
  3287. allowableOverlap = 10000, // About 3 frames @ 30fps
  3288. nearestDistance = Infinity,
  3289. dtsDistance,
  3290. nearestGopObj,
  3291. currentGop,
  3292. currentGopObj,
  3293. i;
  3294. // Search for the GOP nearest to the beginning of this nal unit
  3295. for (i = 0; i < this.gopCache_.length; i++) {
  3296. currentGopObj = this.gopCache_[i];
  3297. currentGop = currentGopObj.gop;
  3298. // Reject Gops with different SPS or PPS
  3299. if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
  3300. !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
  3301. continue;
  3302. }
  3303. // Reject Gops that would require a negative baseMediaDecodeTime
  3304. if (currentGop.dts < track.timelineStartInfo.dts) {
  3305. continue;
  3306. }
  3307. // The distance between the end of the gop and the start of the nalUnit
  3308. dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
  3309. // Only consider GOPS that start before the nal unit and end within
  3310. // a half-second of the nal unit
  3311. if (dtsDistance >= -allowableOverlap &&
  3312. dtsDistance <= halfSecond) {
  3313. // Always use the closest GOP we found if there is more than
  3314. // one candidate
  3315. if (!nearestGopObj ||
  3316. nearestDistance > dtsDistance) {
  3317. nearestGopObj = currentGopObj;
  3318. nearestDistance = dtsDistance;
  3319. }
  3320. }
  3321. }
  3322. if (nearestGopObj) {
  3323. return nearestGopObj.gop;
  3324. }
  3325. return null;
  3326. };
  3327. this.extendFirstKeyFrame_ = function(gops) {
  3328. var currentGop;
  3329. if (!gops[0][0].keyFrame && gops.length > 1) {
  3330. // Remove the first GOP
  3331. currentGop = gops.shift();
  3332. gops.byteLength -= currentGop.byteLength;
  3333. gops.nalCount -= currentGop.nalCount;
  3334. // Extend the first frame of what is now the
  3335. // first gop to cover the time period of the
  3336. // frames we just removed
  3337. gops[0][0].dts = currentGop.dts;
  3338. gops[0][0].pts = currentGop.pts;
  3339. gops[0][0].duration += currentGop.duration;
  3340. }
  3341. return gops;
  3342. };
  3343. // Convert an array of nal units into an array of frames with each frame being
  3344. // composed of the nal units that make up that frame
  3345. // Also keep track of cummulative data about the frame from the nal units such
  3346. // as the frame duration, starting pts, etc.
  3347. this.groupNalsIntoFrames_ = function(nalUnits) {
  3348. var
  3349. i,
  3350. currentNal,
  3351. currentFrame = [],
  3352. frames = [];
  3353. currentFrame.byteLength = 0;
  3354. for (i = 0; i < nalUnits.length; i++) {
  3355. currentNal = nalUnits[i];
  3356. // Split on 'aud'-type nal units
  3357. if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  3358. // Since the very first nal unit is expected to be an AUD
  3359. // only push to the frames array when currentFrame is not empty
  3360. if (currentFrame.length) {
  3361. currentFrame.duration = currentNal.dts - currentFrame.dts;
  3362. frames.push(currentFrame);
  3363. }
  3364. currentFrame = [currentNal];
  3365. currentFrame.byteLength = currentNal.data.byteLength;
  3366. currentFrame.pts = currentNal.pts;
  3367. currentFrame.dts = currentNal.dts;
  3368. } else {
  3369. // Specifically flag key frames for ease of use later
  3370. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  3371. currentFrame.keyFrame = true;
  3372. }
  3373. currentFrame.duration = currentNal.dts - currentFrame.dts;
  3374. currentFrame.byteLength += currentNal.data.byteLength;
  3375. currentFrame.push(currentNal);
  3376. }
  3377. }
  3378. // For the last frame, use the duration of the previous frame if we
  3379. // have nothing better to go on
  3380. if (frames.length &&
  3381. (!currentFrame.duration ||
  3382. currentFrame.duration <= 0)) {
  3383. currentFrame.duration = frames[frames.length - 1].duration;
  3384. }
  3385. // Push the final frame
  3386. frames.push(currentFrame);
  3387. return frames;
  3388. };
  3389. // Convert an array of frames into an array of Gop with each Gop being composed
  3390. // of the frames that make up that Gop
  3391. // Also keep track of cummulative data about the Gop from the frames such as the
  3392. // Gop duration, starting pts, etc.
  3393. this.groupFramesIntoGops_ = function(frames) {
  3394. var
  3395. i,
  3396. currentFrame,
  3397. currentGop = [],
  3398. gops = [];
  3399. // We must pre-set some of the values on the Gop since we
  3400. // keep running totals of these values
  3401. currentGop.byteLength = 0;
  3402. currentGop.nalCount = 0;
  3403. currentGop.duration = 0;
  3404. currentGop.pts = frames[0].pts;
  3405. currentGop.dts = frames[0].dts;
  3406. // store some metadata about all the Gops
  3407. gops.byteLength = 0;
  3408. gops.nalCount = 0;
  3409. gops.duration = 0;
  3410. gops.pts = frames[0].pts;
  3411. gops.dts = frames[0].dts;
  3412. for (i = 0; i < frames.length; i++) {
  3413. currentFrame = frames[i];
  3414. if (currentFrame.keyFrame) {
  3415. // Since the very first frame is expected to be an keyframe
  3416. // only push to the gops array when currentGop is not empty
  3417. if (currentGop.length) {
  3418. gops.push(currentGop);
  3419. gops.byteLength += currentGop.byteLength;
  3420. gops.nalCount += currentGop.nalCount;
  3421. gops.duration += currentGop.duration;
  3422. }
  3423. currentGop = [currentFrame];
  3424. currentGop.nalCount = currentFrame.length;
  3425. currentGop.byteLength = currentFrame.byteLength;
  3426. currentGop.pts = currentFrame.pts;
  3427. currentGop.dts = currentFrame.dts;
  3428. currentGop.duration = currentFrame.duration;
  3429. } else {
  3430. currentGop.duration += currentFrame.duration;
  3431. currentGop.nalCount += currentFrame.length;
  3432. currentGop.byteLength += currentFrame.byteLength;
  3433. currentGop.push(currentFrame);
  3434. }
  3435. }
  3436. if (gops.length && currentGop.duration <= 0) {
  3437. currentGop.duration = gops[gops.length - 1].duration;
  3438. }
  3439. gops.byteLength += currentGop.byteLength;
  3440. gops.nalCount += currentGop.nalCount;
  3441. gops.duration += currentGop.duration;
  3442. // push the final Gop
  3443. gops.push(currentGop);
  3444. return gops;
  3445. };
  3446. // generate the track's sample table from an array of gops
  3447. this.generateSampleTable_ = function(gops, baseDataOffset) {
  3448. var
  3449. h, i,
  3450. sample,
  3451. currentGop,
  3452. currentFrame,
  3453. dataOffset = baseDataOffset || 0,
  3454. samples = [];
  3455. for (h = 0; h < gops.length; h++) {
  3456. currentGop = gops[h];
  3457. for (i = 0; i < currentGop.length; i++) {
  3458. currentFrame = currentGop[i];
  3459. sample = createDefaultSample();
  3460. sample.dataOffset = dataOffset;
  3461. sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
  3462. sample.duration = currentFrame.duration;
  3463. sample.size = 4 * currentFrame.length; // Space for nal unit size
  3464. sample.size += currentFrame.byteLength;
  3465. if (currentFrame.keyFrame) {
  3466. sample.flags.dependsOn = 2;
  3467. }
  3468. dataOffset += sample.size;
  3469. samples.push(sample);
  3470. }
  3471. }
  3472. return samples;
  3473. };
  3474. // generate the track's raw mdat data from an array of gops
  3475. this.concatenateNalData_ = function(gops) {
  3476. var
  3477. h, i, j,
  3478. currentGop,
  3479. currentFrame,
  3480. currentNal,
  3481. dataOffset = 0,
  3482. nalsByteLength = gops.byteLength,
  3483. numberOfNals = gops.nalCount,
  3484. totalByteLength = nalsByteLength + 4 * numberOfNals,
  3485. data = new Uint8Array(totalByteLength),
  3486. view = new DataView(data.buffer);
  3487. // For each Gop..
  3488. for (h = 0; h < gops.length; h++) {
  3489. currentGop = gops[h];
  3490. // For each Frame..
  3491. for (i = 0; i < currentGop.length; i++) {
  3492. currentFrame = currentGop[i];
  3493. // For each NAL..
  3494. for (j = 0; j < currentFrame.length; j++) {
  3495. currentNal = currentFrame[j];
  3496. view.setUint32(dataOffset, currentNal.data.byteLength);
  3497. dataOffset += 4;
  3498. data.set(currentNal.data, dataOffset);
  3499. dataOffset += currentNal.data.byteLength;
  3500. }
  3501. }
  3502. }
  3503. return data;
  3504. };
  3505. // trim gop list to the first gop found that has a matching pts with a gop in the list
  3506. // of gopsToAlignWith starting from the START of the list
  3507. this.alignGopsAtStart_ = function(gops) {
  3508. var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
  3509. byteLength = gops.byteLength;
  3510. nalCount = gops.nalCount;
  3511. duration = gops.duration;
  3512. alignIndex = gopIndex = 0;
  3513. while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
  3514. align = gopsToAlignWith[alignIndex];
  3515. gop = gops[gopIndex];
  3516. if (align.pts === gop.pts) {
  3517. break;
  3518. }
  3519. if (gop.pts > align.pts) {
  3520. // this current gop starts after the current gop we want to align on, so increment
  3521. // align index
  3522. alignIndex++;
  3523. continue;
  3524. }
  3525. // current gop starts before the current gop we want to align on. so increment gop
  3526. // index
  3527. gopIndex++;
  3528. byteLength -= gop.byteLength;
  3529. nalCount -= gop.nalCount;
  3530. duration -= gop.duration;
  3531. }
  3532. if (gopIndex === 0) {
  3533. // no gops to trim
  3534. return gops;
  3535. }
  3536. if (gopIndex === gops.length) {
  3537. // all gops trimmed, skip appending all gops
  3538. return null;
  3539. }
  3540. alignedGops = gops.slice(gopIndex);
  3541. alignedGops.byteLength = byteLength;
  3542. alignedGops.duration = duration;
  3543. alignedGops.nalCount = nalCount;
  3544. alignedGops.pts = alignedGops[0].pts;
  3545. alignedGops.dts = alignedGops[0].dts;
  3546. return alignedGops;
  3547. };
  3548. // trim gop list to the first gop found that has a matching pts with a gop in the list
  3549. // of gopsToAlignWith starting from the END of the list
  3550. this.alignGopsAtEnd_ = function(gops) {
  3551. var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
  3552. alignIndex = gopsToAlignWith.length - 1;
  3553. gopIndex = gops.length - 1;
  3554. alignEndIndex = null;
  3555. matchFound = false;
  3556. while (alignIndex >= 0 && gopIndex >= 0) {
  3557. align = gopsToAlignWith[alignIndex];
  3558. gop = gops[gopIndex];
  3559. if (align.pts === gop.pts) {
  3560. matchFound = true;
  3561. break;
  3562. }
  3563. if (align.pts > gop.pts) {
  3564. alignIndex--;
  3565. continue;
  3566. }
  3567. if (alignIndex === gopsToAlignWith.length - 1) {
  3568. // gop.pts is greater than the last alignment candidate. If no match is found
  3569. // by the end of this loop, we still want to append gops that come after this
  3570. // point
  3571. alignEndIndex = gopIndex;
  3572. }
  3573. gopIndex--;
  3574. }
  3575. if (!matchFound && alignEndIndex === null) {
  3576. return null;
  3577. }
  3578. var trimIndex;
  3579. if (matchFound) {
  3580. trimIndex = gopIndex;
  3581. } else {
  3582. trimIndex = alignEndIndex;
  3583. }
  3584. if (trimIndex === 0) {
  3585. return gops;
  3586. }
  3587. var alignedGops = gops.slice(trimIndex);
  3588. var metadata = alignedGops.reduce(function(total, gop) {
  3589. total.byteLength += gop.byteLength;
  3590. total.duration += gop.duration;
  3591. total.nalCount += gop.nalCount;
  3592. return total;
  3593. }, { byteLength: 0, duration: 0, nalCount: 0 });
  3594. alignedGops.byteLength = metadata.byteLength;
  3595. alignedGops.duration = metadata.duration;
  3596. alignedGops.nalCount = metadata.nalCount;
  3597. alignedGops.pts = alignedGops[0].pts;
  3598. alignedGops.dts = alignedGops[0].dts;
  3599. return alignedGops;
  3600. };
  3601. this.alignGopsWith = function(newGopsToAlignWith) {
  3602. gopsToAlignWith = newGopsToAlignWith;
  3603. };
  3604. };
  3605. VideoSegmentStream.prototype = new Stream();
  3606. /**
  3607. * Store information about the start and end of the track and the
  3608. * duration for each frame/sample we process in order to calculate
  3609. * the baseMediaDecodeTime
  3610. */
  3611. collectDtsInfo = function(track, data) {
  3612. if (typeof data.pts === 'number') {
  3613. if (track.timelineStartInfo.pts === undefined) {
  3614. track.timelineStartInfo.pts = data.pts;
  3615. }
  3616. if (track.minSegmentPts === undefined) {
  3617. track.minSegmentPts = data.pts;
  3618. } else {
  3619. track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
  3620. }
  3621. if (track.maxSegmentPts === undefined) {
  3622. track.maxSegmentPts = data.pts;
  3623. } else {
  3624. track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
  3625. }
  3626. }
  3627. if (typeof data.dts === 'number') {
  3628. if (track.timelineStartInfo.dts === undefined) {
  3629. track.timelineStartInfo.dts = data.dts;
  3630. }
  3631. if (track.minSegmentDts === undefined) {
  3632. track.minSegmentDts = data.dts;
  3633. } else {
  3634. track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
  3635. }
  3636. if (track.maxSegmentDts === undefined) {
  3637. track.maxSegmentDts = data.dts;
  3638. } else {
  3639. track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
  3640. }
  3641. }
  3642. };
  3643. /**
  3644. * Clear values used to calculate the baseMediaDecodeTime between
  3645. * tracks
  3646. */
  3647. clearDtsInfo = function(track) {
  3648. delete track.minSegmentDts;
  3649. delete track.maxSegmentDts;
  3650. delete track.minSegmentPts;
  3651. delete track.maxSegmentPts;
  3652. };
  3653. /**
  3654. * Calculate the track's baseMediaDecodeTime based on the earliest
  3655. * DTS the transmuxer has ever seen and the minimum DTS for the
  3656. * current track
  3657. */
  3658. calculateTrackBaseMediaDecodeTime = function(track) {
  3659. var
  3660. baseMediaDecodeTime,
  3661. scale,
  3662. // Calculate the distance, in time, that this segment starts from the start
  3663. // of the timeline (earliest time seen since the transmuxer initialized)
  3664. timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
  3665. // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  3666. // we want the start of the first segment to be placed
  3667. baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
  3668. // Add to that the distance this segment is from the very first
  3669. baseMediaDecodeTime += timeSinceStartOfTimeline;
  3670. // baseMediaDecodeTime must not become negative
  3671. baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
  3672. if (track.type === 'audio') {
  3673. // Audio has a different clock equal to the sampling_rate so we need to
  3674. // scale the PTS values into the clock rate of the track
  3675. scale = track.samplerate / ONE_SECOND_IN_TS;
  3676. baseMediaDecodeTime *= scale;
  3677. baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
  3678. }
  3679. return baseMediaDecodeTime;
  3680. };
  3681. /**
  3682. * A Stream that can combine multiple streams (ie. audio & video)
  3683. * into a single output segment for MSE. Also supports audio-only
  3684. * and video-only streams.
  3685. */
  3686. CoalesceStream = function(options, metadataStream) {
  3687. // Number of Tracks per output segment
  3688. // If greater than 1, we combine multiple
  3689. // tracks into a single segment
  3690. this.numberOfTracks = 0;
  3691. this.metadataStream = metadataStream;
  3692. if (typeof options.remux !== 'undefined') {
  3693. this.remuxTracks = !!options.remux;
  3694. } else {
  3695. this.remuxTracks = true;
  3696. }
  3697. this.pendingTracks = [];
  3698. this.videoTrack = null;
  3699. this.pendingBoxes = [];
  3700. this.pendingCaptions = [];
  3701. this.pendingMetadata = [];
  3702. this.pendingBytes = 0;
  3703. this.emittedTracks = 0;
  3704. CoalesceStream.prototype.init.call(this);
  3705. // Take output from multiple
  3706. this.push = function(output) {
  3707. // buffer incoming captions until the associated video segment
  3708. // finishes
  3709. if (output.text) {
  3710. return this.pendingCaptions.push(output);
  3711. }
  3712. // buffer incoming id3 tags until the final flush
  3713. if (output.frames) {
  3714. return this.pendingMetadata.push(output);
  3715. }
  3716. // Add this track to the list of pending tracks and store
  3717. // important information required for the construction of
  3718. // the final segment
  3719. this.pendingTracks.push(output.track);
  3720. this.pendingBoxes.push(output.boxes);
  3721. this.pendingBytes += output.boxes.byteLength;
  3722. if (output.track.type === 'video') {
  3723. this.videoTrack = output.track;
  3724. }
  3725. if (output.track.type === 'audio') {
  3726. this.audioTrack = output.track;
  3727. }
  3728. };
  3729. };
  3730. CoalesceStream.prototype = new Stream();
  3731. CoalesceStream.prototype.flush = function(flushSource) {
  3732. var
  3733. offset = 0,
  3734. event = {
  3735. captions: [],
  3736. captionStreams: {},
  3737. metadata: [],
  3738. info: {}
  3739. },
  3740. caption,
  3741. id3,
  3742. initSegment,
  3743. timelineStartPts = 0,
  3744. i;
  3745. if (this.pendingTracks.length < this.numberOfTracks) {
  3746. if (flushSource !== 'VideoSegmentStream' &&
  3747. flushSource !== 'AudioSegmentStream') {
  3748. // Return because we haven't received a flush from a data-generating
  3749. // portion of the segment (meaning that we have only recieved meta-data
  3750. // or captions.)
  3751. return;
  3752. } else if (this.remuxTracks) {
  3753. // Return until we have enough tracks from the pipeline to remux (if we
  3754. // are remuxing audio and video into a single MP4)
  3755. return;
  3756. } else if (this.pendingTracks.length === 0) {
  3757. // In the case where we receive a flush without any data having been
  3758. // received we consider it an emitted track for the purposes of coalescing
  3759. // `done` events.
  3760. // We do this for the case where there is an audio and video track in the
  3761. // segment but no audio data. (seen in several playlists with alternate
  3762. // audio tracks and no audio present in the main TS segments.)
  3763. this.emittedTracks++;
  3764. if (this.emittedTracks >= this.numberOfTracks) {
  3765. this.trigger('done');
  3766. this.emittedTracks = 0;
  3767. }
  3768. return;
  3769. }
  3770. }
  3771. if (this.videoTrack) {
  3772. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  3773. VIDEO_PROPERTIES.forEach(function(prop) {
  3774. event.info[prop] = this.videoTrack[prop];
  3775. }, this);
  3776. } else if (this.audioTrack) {
  3777. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  3778. AUDIO_PROPERTIES.forEach(function(prop) {
  3779. event.info[prop] = this.audioTrack[prop];
  3780. }, this);
  3781. }
  3782. if (this.pendingTracks.length === 1) {
  3783. event.type = this.pendingTracks[0].type;
  3784. } else {
  3785. event.type = 'combined';
  3786. }
  3787. this.emittedTracks += this.pendingTracks.length;
  3788. initSegment = mp4.initSegment(this.pendingTracks);
  3789. // Create a new typed array to hold the init segment
  3790. event.initSegment = new Uint8Array(initSegment.byteLength);
  3791. // Create an init segment containing a moov
  3792. // and track definitions
  3793. event.initSegment.set(initSegment);
  3794. // Create a new typed array to hold the moof+mdats
  3795. event.data = new Uint8Array(this.pendingBytes);
  3796. // Append each moof+mdat (one per track) together
  3797. for (i = 0; i < this.pendingBoxes.length; i++) {
  3798. event.data.set(this.pendingBoxes[i], offset);
  3799. offset += this.pendingBoxes[i].byteLength;
  3800. }
  3801. // Translate caption PTS times into second offsets into the
  3802. // video timeline for the segment, and add track info
  3803. for (i = 0; i < this.pendingCaptions.length; i++) {
  3804. caption = this.pendingCaptions[i];
  3805. caption.startTime = (caption.startPts - timelineStartPts);
  3806. caption.startTime /= 90e3;
  3807. caption.endTime = (caption.endPts - timelineStartPts);
  3808. caption.endTime /= 90e3;
  3809. event.captionStreams[caption.stream] = true;
  3810. event.captions.push(caption);
  3811. }
  3812. // Translate ID3 frame PTS times into second offsets into the
  3813. // video timeline for the segment
  3814. for (i = 0; i < this.pendingMetadata.length; i++) {
  3815. id3 = this.pendingMetadata[i];
  3816. id3.cueTime = (id3.pts - timelineStartPts);
  3817. id3.cueTime /= 90e3;
  3818. event.metadata.push(id3);
  3819. }
  3820. // We add this to every single emitted segment even though we only need
  3821. // it for the first
  3822. event.metadata.dispatchType = this.metadataStream.dispatchType;
  3823. // Reset stream state
  3824. this.pendingTracks.length = 0;
  3825. this.videoTrack = null;
  3826. this.pendingBoxes.length = 0;
  3827. this.pendingCaptions.length = 0;
  3828. this.pendingBytes = 0;
  3829. this.pendingMetadata.length = 0;
  3830. // Emit the built segment
  3831. this.trigger('data', event);
  3832. // Only emit `done` if all tracks have been flushed and emitted
  3833. if (this.emittedTracks >= this.numberOfTracks) {
  3834. this.trigger('done');
  3835. this.emittedTracks = 0;
  3836. }
  3837. };
  3838. /**
  3839. * A Stream that expects MP2T binary data as input and produces
  3840. * corresponding media segments, suitable for use with Media Source
  3841. * Extension (MSE) implementations that support the ISO BMFF byte
  3842. * stream format, like Chrome.
  3843. */
  3844. Transmuxer = function(options) {
  3845. var
  3846. self = this,
  3847. hasFlushed = true,
  3848. videoTrack,
  3849. audioTrack;
  3850. Transmuxer.prototype.init.call(this);
  3851. options = options || {};
  3852. this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  3853. this.transmuxPipeline_ = {};
  3854. this.setupAacPipeline = function() {
  3855. var pipeline = {};
  3856. this.transmuxPipeline_ = pipeline;
  3857. pipeline.type = 'aac';
  3858. pipeline.metadataStream = new m2ts.MetadataStream();
  3859. // set up the parsing pipeline
  3860. pipeline.aacStream = new AacStream();
  3861. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  3862. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  3863. pipeline.adtsStream = new AdtsStream();
  3864. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  3865. pipeline.headOfPipeline = pipeline.aacStream;
  3866. pipeline.aacStream
  3867. .pipe(pipeline.audioTimestampRolloverStream)
  3868. .pipe(pipeline.adtsStream);
  3869. pipeline.aacStream
  3870. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  3871. .pipe(pipeline.metadataStream)
  3872. .pipe(pipeline.coalesceStream);
  3873. pipeline.metadataStream.on('timestamp', function(frame) {
  3874. pipeline.aacStream.setTimestamp(frame.timeStamp);
  3875. });
  3876. pipeline.aacStream.on('data', function(data) {
  3877. if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
  3878. audioTrack = audioTrack || {
  3879. timelineStartInfo: {
  3880. baseMediaDecodeTime: self.baseMediaDecodeTime
  3881. },
  3882. codec: 'adts',
  3883. type: 'audio'
  3884. };
  3885. // hook up the audio segment stream to the first track with aac data
  3886. pipeline.coalesceStream.numberOfTracks++;
  3887. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  3888. // Set up the final part of the audio pipeline
  3889. pipeline.adtsStream
  3890. .pipe(pipeline.audioSegmentStream)
  3891. .pipe(pipeline.coalesceStream);
  3892. }
  3893. });
  3894. // Re-emit any data coming from the coalesce stream to the outside world
  3895. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  3896. // Let the consumer know we have finished flushing the entire pipeline
  3897. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  3898. };
  3899. this.setupTsPipeline = function() {
  3900. var pipeline = {};
  3901. this.transmuxPipeline_ = pipeline;
  3902. pipeline.type = 'ts';
  3903. pipeline.metadataStream = new m2ts.MetadataStream();
  3904. // set up the parsing pipeline
  3905. pipeline.packetStream = new m2ts.TransportPacketStream();
  3906. pipeline.parseStream = new m2ts.TransportParseStream();
  3907. pipeline.elementaryStream = new m2ts.ElementaryStream();
  3908. pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  3909. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  3910. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  3911. pipeline.adtsStream = new AdtsStream();
  3912. pipeline.h264Stream = new H264Stream();
  3913. pipeline.captionStream = new m2ts.CaptionStream();
  3914. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  3915. pipeline.headOfPipeline = pipeline.packetStream;
  3916. // disassemble MPEG2-TS packets into elementary streams
  3917. pipeline.packetStream
  3918. .pipe(pipeline.parseStream)
  3919. .pipe(pipeline.elementaryStream);
  3920. // !!THIS ORDER IS IMPORTANT!!
  3921. // demux the streams
  3922. pipeline.elementaryStream
  3923. .pipe(pipeline.videoTimestampRolloverStream)
  3924. .pipe(pipeline.h264Stream);
  3925. pipeline.elementaryStream
  3926. .pipe(pipeline.audioTimestampRolloverStream)
  3927. .pipe(pipeline.adtsStream);
  3928. pipeline.elementaryStream
  3929. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  3930. .pipe(pipeline.metadataStream)
  3931. .pipe(pipeline.coalesceStream);
  3932. // Hook up CEA-608/708 caption stream
  3933. pipeline.h264Stream.pipe(pipeline.captionStream)
  3934. .pipe(pipeline.coalesceStream);
  3935. pipeline.elementaryStream.on('data', function(data) {
  3936. var i;
  3937. if (data.type === 'metadata') {
  3938. i = data.tracks.length;
  3939. // scan the tracks listed in the metadata
  3940. while (i--) {
  3941. if (!videoTrack && data.tracks[i].type === 'video') {
  3942. videoTrack = data.tracks[i];
  3943. videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  3944. } else if (!audioTrack && data.tracks[i].type === 'audio') {
  3945. audioTrack = data.tracks[i];
  3946. audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  3947. }
  3948. }
  3949. // hook up the video segment stream to the first track with h264 data
  3950. if (videoTrack && !pipeline.videoSegmentStream) {
  3951. pipeline.coalesceStream.numberOfTracks++;
  3952. pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack, options);
  3953. pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
  3954. // When video emits timelineStartInfo data after a flush, we forward that
  3955. // info to the AudioSegmentStream, if it exists, because video timeline
  3956. // data takes precedence.
  3957. if (audioTrack) {
  3958. audioTrack.timelineStartInfo = timelineStartInfo;
  3959. // On the first segment we trim AAC frames that exist before the
  3960. // very earliest DTS we have seen in video because Chrome will
  3961. // interpret any video track with a baseMediaDecodeTime that is
  3962. // non-zero as a gap.
  3963. pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
  3964. }
  3965. });
  3966. pipeline.videoSegmentStream.on('processedGopsInfo',
  3967. self.trigger.bind(self, 'gopInfo'));
  3968. pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
  3969. if (audioTrack) {
  3970. pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
  3971. }
  3972. });
  3973. // Set up the final part of the video pipeline
  3974. pipeline.h264Stream
  3975. .pipe(pipeline.videoSegmentStream)
  3976. .pipe(pipeline.coalesceStream);
  3977. }
  3978. if (audioTrack && !pipeline.audioSegmentStream) {
  3979. // hook up the audio segment stream to the first track with aac data
  3980. pipeline.coalesceStream.numberOfTracks++;
  3981. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  3982. // Set up the final part of the audio pipeline
  3983. pipeline.adtsStream
  3984. .pipe(pipeline.audioSegmentStream)
  3985. .pipe(pipeline.coalesceStream);
  3986. }
  3987. }
  3988. });
  3989. // Re-emit any data coming from the coalesce stream to the outside world
  3990. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  3991. // Let the consumer know we have finished flushing the entire pipeline
  3992. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  3993. };
  3994. // hook up the segment streams once track metadata is delivered
  3995. this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  3996. var pipeline = this.transmuxPipeline_;
  3997. this.baseMediaDecodeTime = baseMediaDecodeTime;
  3998. if (audioTrack) {
  3999. audioTrack.timelineStartInfo.dts = undefined;
  4000. audioTrack.timelineStartInfo.pts = undefined;
  4001. clearDtsInfo(audioTrack);
  4002. audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  4003. if (pipeline.audioTimestampRolloverStream) {
  4004. pipeline.audioTimestampRolloverStream.discontinuity();
  4005. }
  4006. }
  4007. if (videoTrack) {
  4008. if (pipeline.videoSegmentStream) {
  4009. pipeline.videoSegmentStream.gopCache_ = [];
  4010. pipeline.videoTimestampRolloverStream.discontinuity();
  4011. }
  4012. videoTrack.timelineStartInfo.dts = undefined;
  4013. videoTrack.timelineStartInfo.pts = undefined;
  4014. clearDtsInfo(videoTrack);
  4015. pipeline.captionStream.reset();
  4016. videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  4017. }
  4018. if (pipeline.timedMetadataTimestampRolloverStream) {
  4019. pipeline.timedMetadataTimestampRolloverStream.discontinuity();
  4020. }
  4021. };
  4022. this.setAudioAppendStart = function(timestamp) {
  4023. if (audioTrack) {
  4024. this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
  4025. }
  4026. };
  4027. this.alignGopsWith = function(gopsToAlignWith) {
  4028. if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
  4029. this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
  4030. }
  4031. };
  4032. // feed incoming data to the front of the parsing pipeline
  4033. this.push = function(data) {
  4034. if (hasFlushed) {
  4035. var isAac = isLikelyAacData(data);
  4036. if (isAac && this.transmuxPipeline_.type !== 'aac') {
  4037. this.setupAacPipeline();
  4038. } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
  4039. this.setupTsPipeline();
  4040. }
  4041. hasFlushed = false;
  4042. }
  4043. this.transmuxPipeline_.headOfPipeline.push(data);
  4044. };
  4045. // flush any buffered data
  4046. this.flush = function() {
  4047. hasFlushed = true;
  4048. // Start at the top of the pipeline and flush all pending work
  4049. this.transmuxPipeline_.headOfPipeline.flush();
  4050. };
  4051. // Caption data has to be reset when seeking outside buffered range
  4052. this.resetCaptions = function() {
  4053. if (this.transmuxPipeline_.captionStream) {
  4054. this.transmuxPipeline_.captionStream.reset();
  4055. }
  4056. };
  4057. };
  4058. Transmuxer.prototype = new Stream();
  4059. module.exports = {
  4060. Transmuxer: Transmuxer,
  4061. VideoSegmentStream: VideoSegmentStream,
  4062. AudioSegmentStream: AudioSegmentStream,
  4063. AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  4064. VIDEO_PROPERTIES: VIDEO_PROPERTIES
  4065. };
  4066. },{"../aac":1,"../codecs/adts.js":2,"../codecs/h264":3,"../data/silence":4,"../m2ts/m2ts.js":6,"../utils/clock":13,"../utils/stream.js":15,"./mp4-generator.js":11}],13:[function(require,module,exports){
  4067. var
  4068. ONE_SECOND_IN_TS = 90000, // 90kHz clock
  4069. secondsToVideoTs,
  4070. secondsToAudioTs,
  4071. videoTsToSeconds,
  4072. audioTsToSeconds,
  4073. audioTsToVideoTs,
  4074. videoTsToAudioTs;
  4075. secondsToVideoTs = function(seconds) {
  4076. return seconds * ONE_SECOND_IN_TS;
  4077. };
  4078. secondsToAudioTs = function(seconds, sampleRate) {
  4079. return seconds * sampleRate;
  4080. };
  4081. videoTsToSeconds = function(timestamp) {
  4082. return timestamp / ONE_SECOND_IN_TS;
  4083. };
  4084. audioTsToSeconds = function(timestamp, sampleRate) {
  4085. return timestamp / sampleRate;
  4086. };
  4087. audioTsToVideoTs = function(timestamp, sampleRate) {
  4088. return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
  4089. };
  4090. videoTsToAudioTs = function(timestamp, sampleRate) {
  4091. return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
  4092. };
  4093. module.exports = {
  4094. secondsToVideoTs: secondsToVideoTs,
  4095. secondsToAudioTs: secondsToAudioTs,
  4096. videoTsToSeconds: videoTsToSeconds,
  4097. audioTsToSeconds: audioTsToSeconds,
  4098. audioTsToVideoTs: audioTsToVideoTs,
  4099. videoTsToAudioTs: videoTsToAudioTs
  4100. };
  4101. },{}],14:[function(require,module,exports){
  4102. 'use strict';
  4103. var ExpGolomb;
  4104. /**
  4105. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  4106. * scheme used by h264.
  4107. */
  4108. ExpGolomb = function(workingData) {
  4109. var
  4110. // the number of bytes left to examine in workingData
  4111. workingBytesAvailable = workingData.byteLength,
  4112. // the current word being examined
  4113. workingWord = 0, // :uint
  4114. // the number of bits left to examine in the current word
  4115. workingBitsAvailable = 0; // :uint;
  4116. // ():uint
  4117. this.length = function() {
  4118. return (8 * workingBytesAvailable);
  4119. };
  4120. // ():uint
  4121. this.bitsAvailable = function() {
  4122. return (8 * workingBytesAvailable) + workingBitsAvailable;
  4123. };
  4124. // ():void
  4125. this.loadWord = function() {
  4126. var
  4127. position = workingData.byteLength - workingBytesAvailable,
  4128. workingBytes = new Uint8Array(4),
  4129. availableBytes = Math.min(4, workingBytesAvailable);
  4130. if (availableBytes === 0) {
  4131. throw new Error('no bytes available');
  4132. }
  4133. workingBytes.set(workingData.subarray(position,
  4134. position + availableBytes));
  4135. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  4136. // track the amount of workingData that has been processed
  4137. workingBitsAvailable = availableBytes * 8;
  4138. workingBytesAvailable -= availableBytes;
  4139. };
  4140. // (count:int):void
  4141. this.skipBits = function(count) {
  4142. var skipBytes; // :int
  4143. if (workingBitsAvailable > count) {
  4144. workingWord <<= count;
  4145. workingBitsAvailable -= count;
  4146. } else {
  4147. count -= workingBitsAvailable;
  4148. skipBytes = Math.floor(count / 8);
  4149. count -= (skipBytes * 8);
  4150. workingBytesAvailable -= skipBytes;
  4151. this.loadWord();
  4152. workingWord <<= count;
  4153. workingBitsAvailable -= count;
  4154. }
  4155. };
  4156. // (size:int):uint
  4157. this.readBits = function(size) {
  4158. var
  4159. bits = Math.min(workingBitsAvailable, size), // :uint
  4160. valu = workingWord >>> (32 - bits); // :uint
  4161. // if size > 31, handle error
  4162. workingBitsAvailable -= bits;
  4163. if (workingBitsAvailable > 0) {
  4164. workingWord <<= bits;
  4165. } else if (workingBytesAvailable > 0) {
  4166. this.loadWord();
  4167. }
  4168. bits = size - bits;
  4169. if (bits > 0) {
  4170. return valu << bits | this.readBits(bits);
  4171. }
  4172. return valu;
  4173. };
  4174. // ():uint
  4175. this.skipLeadingZeros = function() {
  4176. var leadingZeroCount; // :uint
  4177. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  4178. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  4179. // the first bit of working word is 1
  4180. workingWord <<= leadingZeroCount;
  4181. workingBitsAvailable -= leadingZeroCount;
  4182. return leadingZeroCount;
  4183. }
  4184. }
  4185. // we exhausted workingWord and still have not found a 1
  4186. this.loadWord();
  4187. return leadingZeroCount + this.skipLeadingZeros();
  4188. };
  4189. // ():void
  4190. this.skipUnsignedExpGolomb = function() {
  4191. this.skipBits(1 + this.skipLeadingZeros());
  4192. };
  4193. // ():void
  4194. this.skipExpGolomb = function() {
  4195. this.skipBits(1 + this.skipLeadingZeros());
  4196. };
  4197. // ():uint
  4198. this.readUnsignedExpGolomb = function() {
  4199. var clz = this.skipLeadingZeros(); // :uint
  4200. return this.readBits(clz + 1) - 1;
  4201. };
  4202. // ():int
  4203. this.readExpGolomb = function() {
  4204. var valu = this.readUnsignedExpGolomb(); // :int
  4205. if (0x01 & valu) {
  4206. // the number is odd if the low order bit is set
  4207. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  4208. }
  4209. return -1 * (valu >>> 1); // divide by two then make it negative
  4210. };
  4211. // Some convenience functions
  4212. // :Boolean
  4213. this.readBoolean = function() {
  4214. return this.readBits(1) === 1;
  4215. };
  4216. // ():int
  4217. this.readUnsignedByte = function() {
  4218. return this.readBits(8);
  4219. };
  4220. this.loadWord();
  4221. };
  4222. module.exports = ExpGolomb;
  4223. },{}],15:[function(require,module,exports){
  4224. /**
  4225. * mux.js
  4226. *
  4227. * Copyright (c) 2014 Brightcove
  4228. * All rights reserved.
  4229. *
  4230. * A lightweight readable stream implemention that handles event dispatching.
  4231. * Objects that inherit from streams should call init in their constructors.
  4232. */
  4233. 'use strict';
  4234. var Stream = function() {
  4235. this.init = function() {
  4236. var listeners = {};
  4237. /**
  4238. * Add a listener for a specified event type.
  4239. * @param type {string} the event name
  4240. * @param listener {function} the callback to be invoked when an event of
  4241. * the specified type occurs
  4242. */
  4243. this.on = function(type, listener) {
  4244. if (!listeners[type]) {
  4245. listeners[type] = [];
  4246. }
  4247. listeners[type] = listeners[type].concat(listener);
  4248. };
  4249. /**
  4250. * Remove a listener for a specified event type.
  4251. * @param type {string} the event name
  4252. * @param listener {function} a function previously registered for this
  4253. * type of event through `on`
  4254. */
  4255. this.off = function(type, listener) {
  4256. var index;
  4257. if (!listeners[type]) {
  4258. return false;
  4259. }
  4260. index = listeners[type].indexOf(listener);
  4261. listeners[type] = listeners[type].slice();
  4262. listeners[type].splice(index, 1);
  4263. return index > -1;
  4264. };
  4265. /**
  4266. * Trigger an event of the specified type on this stream. Any additional
  4267. * arguments to this function are passed as parameters to event listeners.
  4268. * @param type {string} the event name
  4269. */
  4270. this.trigger = function(type) {
  4271. var callbacks, i, length, args;
  4272. callbacks = listeners[type];
  4273. if (!callbacks) {
  4274. return;
  4275. }
  4276. // Slicing the arguments on every invocation of this method
  4277. // can add a significant amount of overhead. Avoid the
  4278. // intermediate object creation for the common case of a
  4279. // single callback argument
  4280. if (arguments.length === 2) {
  4281. length = callbacks.length;
  4282. for (i = 0; i < length; ++i) {
  4283. callbacks[i].call(this, arguments[1]);
  4284. }
  4285. } else {
  4286. args = [];
  4287. i = arguments.length;
  4288. for (i = 1; i < arguments.length; ++i) {
  4289. args.push(arguments[i]);
  4290. }
  4291. length = callbacks.length;
  4292. for (i = 0; i < length; ++i) {
  4293. callbacks[i].apply(this, args);
  4294. }
  4295. }
  4296. };
  4297. /**
  4298. * Destroys the stream and cleans up.
  4299. */
  4300. this.dispose = function() {
  4301. listeners = {};
  4302. };
  4303. };
  4304. };
  4305. /**
  4306. * Forwards all `data` events on this stream to the destination stream. The
  4307. * destination stream should provide a method `push` to receive the data
  4308. * events as they arrive.
  4309. * @param destination {stream} the stream that will receive all `data` events
  4310. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  4311. * when the current stream emits a 'done' event
  4312. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  4313. */
  4314. Stream.prototype.pipe = function(destination) {
  4315. this.on('data', function(data) {
  4316. destination.push(data);
  4317. });
  4318. this.on('done', function(flushSource) {
  4319. destination.flush(flushSource);
  4320. });
  4321. return destination;
  4322. };
  4323. // Default stream functions that are expected to be overridden to perform
  4324. // actual work. These are provided by the prototype as a sort of no-op
  4325. // implementation so that we don't have to check for their existence in the
  4326. // `pipe` function above.
  4327. Stream.prototype.push = function(data) {
  4328. this.trigger('data', data);
  4329. };
  4330. Stream.prototype.flush = function(flushSource) {
  4331. this.trigger('done', flushSource);
  4332. };
  4333. module.exports = Stream;
  4334. },{}]},{},[10])(10)
  4335. });