mux-flv.js 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601
  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. 'use strict';
  3. var Stream = require('../utils/stream.js');
  4. var AdtsStream;
  5. var
  6. ADTS_SAMPLING_FREQUENCIES = [
  7. 96000,
  8. 88200,
  9. 64000,
  10. 48000,
  11. 44100,
  12. 32000,
  13. 24000,
  14. 22050,
  15. 16000,
  16. 12000,
  17. 11025,
  18. 8000,
  19. 7350
  20. ];
  21. /*
  22. * Accepts a ElementaryStream and emits data events with parsed
  23. * AAC Audio Frames of the individual packets. Input audio in ADTS
  24. * format is unpacked and re-emitted as AAC frames.
  25. *
  26. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  27. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  28. */
  29. AdtsStream = function() {
  30. var buffer;
  31. AdtsStream.prototype.init.call(this);
  32. this.push = function(packet) {
  33. var
  34. i = 0,
  35. frameNum = 0,
  36. frameLength,
  37. protectionSkipBytes,
  38. frameEnd,
  39. oldBuffer,
  40. sampleCount,
  41. adtsFrameDuration;
  42. if (packet.type !== 'audio') {
  43. // ignore non-audio data
  44. return;
  45. }
  46. // Prepend any data in the buffer to the input data so that we can parse
  47. // aac frames the cross a PES packet boundary
  48. if (buffer) {
  49. oldBuffer = buffer;
  50. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  51. buffer.set(oldBuffer);
  52. buffer.set(packet.data, oldBuffer.byteLength);
  53. } else {
  54. buffer = packet.data;
  55. }
  56. // unpack any ADTS frames which have been fully received
  57. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  58. while (i + 5 < buffer.length) {
  59. // Loook for the start of an ADTS header..
  60. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  61. // If a valid header was not found, jump one forward and attempt to
  62. // find a valid ADTS header starting at the next byte
  63. i++;
  64. continue;
  65. }
  66. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  67. // end of the ADTS header
  68. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  69. // Frame length is a 13 bit integer starting 16 bits from the
  70. // end of the sync sequence
  71. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  72. (buffer[i + 4] << 3) |
  73. ((buffer[i + 5] & 0xe0) >> 5);
  74. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  75. adtsFrameDuration = (sampleCount * 90000) /
  76. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  77. frameEnd = i + frameLength;
  78. // If we don't have enough data to actually finish this ADTS frame, return
  79. // and wait for more data
  80. if (buffer.byteLength < frameEnd) {
  81. return;
  82. }
  83. // Otherwise, deliver the complete AAC frame
  84. this.trigger('data', {
  85. pts: packet.pts + (frameNum * adtsFrameDuration),
  86. dts: packet.dts + (frameNum * adtsFrameDuration),
  87. sampleCount: sampleCount,
  88. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  89. channelcount: ((buffer[i + 2] & 1) << 2) |
  90. ((buffer[i + 3] & 0xc0) >>> 6),
  91. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  92. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  93. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  94. samplesize: 16,
  95. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  96. });
  97. // If the buffer is empty, clear it and return
  98. if (buffer.byteLength === frameEnd) {
  99. buffer = undefined;
  100. return;
  101. }
  102. frameNum++;
  103. // Remove the finished frame from the buffer and start the process again
  104. buffer = buffer.subarray(frameEnd);
  105. }
  106. };
  107. this.flush = function() {
  108. this.trigger('done');
  109. };
  110. };
  111. AdtsStream.prototype = new Stream();
  112. module.exports = AdtsStream;
  113. },{"../utils/stream.js":15}],2:[function(require,module,exports){
  114. 'use strict';
  115. var Stream = require('../utils/stream.js');
  116. var ExpGolomb = require('../utils/exp-golomb.js');
  117. var H264Stream, NalByteStream;
  118. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  119. /**
  120. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  121. */
  122. NalByteStream = function() {
  123. var
  124. syncPoint = 0,
  125. i,
  126. buffer;
  127. NalByteStream.prototype.init.call(this);
  128. this.push = function(data) {
  129. var swapBuffer;
  130. if (!buffer) {
  131. buffer = data.data;
  132. } else {
  133. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  134. swapBuffer.set(buffer);
  135. swapBuffer.set(data.data, buffer.byteLength);
  136. buffer = swapBuffer;
  137. }
  138. // Rec. ITU-T H.264, Annex B
  139. // scan for NAL unit boundaries
  140. // a match looks like this:
  141. // 0 0 1 .. NAL .. 0 0 1
  142. // ^ sync point ^ i
  143. // or this:
  144. // 0 0 1 .. NAL .. 0 0 0
  145. // ^ sync point ^ i
  146. // advance the sync point to a NAL start, if necessary
  147. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  148. if (buffer[syncPoint + 2] === 1) {
  149. // the sync point is properly aligned
  150. i = syncPoint + 5;
  151. break;
  152. }
  153. }
  154. while (i < buffer.byteLength) {
  155. // look at the current byte to determine if we've hit the end of
  156. // a NAL unit boundary
  157. switch (buffer[i]) {
  158. case 0:
  159. // skip past non-sync sequences
  160. if (buffer[i - 1] !== 0) {
  161. i += 2;
  162. break;
  163. } else if (buffer[i - 2] !== 0) {
  164. i++;
  165. break;
  166. }
  167. // deliver the NAL unit if it isn't empty
  168. if (syncPoint + 3 !== i - 2) {
  169. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  170. }
  171. // drop trailing zeroes
  172. do {
  173. i++;
  174. } while (buffer[i] !== 1 && i < buffer.length);
  175. syncPoint = i - 2;
  176. i += 3;
  177. break;
  178. case 1:
  179. // skip past non-sync sequences
  180. if (buffer[i - 1] !== 0 ||
  181. buffer[i - 2] !== 0) {
  182. i += 3;
  183. break;
  184. }
  185. // deliver the NAL unit
  186. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  187. syncPoint = i - 2;
  188. i += 3;
  189. break;
  190. default:
  191. // the current byte isn't a one or zero, so it cannot be part
  192. // of a sync sequence
  193. i += 3;
  194. break;
  195. }
  196. }
  197. // filter out the NAL units that were delivered
  198. buffer = buffer.subarray(syncPoint);
  199. i -= syncPoint;
  200. syncPoint = 0;
  201. };
  202. this.flush = function() {
  203. // deliver the last buffered NAL unit
  204. if (buffer && buffer.byteLength > 3) {
  205. this.trigger('data', buffer.subarray(syncPoint + 3));
  206. }
  207. // reset the stream state
  208. buffer = null;
  209. syncPoint = 0;
  210. this.trigger('done');
  211. };
  212. };
  213. NalByteStream.prototype = new Stream();
  214. // values of profile_idc that indicate additional fields are included in the SPS
  215. // see Recommendation ITU-T H.264 (4/2013),
  216. // 7.3.2.1.1 Sequence parameter set data syntax
  217. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  218. 100: true,
  219. 110: true,
  220. 122: true,
  221. 244: true,
  222. 44: true,
  223. 83: true,
  224. 86: true,
  225. 118: true,
  226. 128: true,
  227. 138: true,
  228. 139: true,
  229. 134: true
  230. };
  231. /**
  232. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  233. * events.
  234. */
  235. H264Stream = function() {
  236. var
  237. nalByteStream = new NalByteStream(),
  238. self,
  239. trackId,
  240. currentPts,
  241. currentDts,
  242. discardEmulationPreventionBytes,
  243. readSequenceParameterSet,
  244. skipScalingList;
  245. H264Stream.prototype.init.call(this);
  246. self = this;
  247. this.push = function(packet) {
  248. if (packet.type !== 'video') {
  249. return;
  250. }
  251. trackId = packet.trackId;
  252. currentPts = packet.pts;
  253. currentDts = packet.dts;
  254. nalByteStream.push(packet);
  255. };
  256. nalByteStream.on('data', function(data) {
  257. var
  258. event = {
  259. trackId: trackId,
  260. pts: currentPts,
  261. dts: currentDts,
  262. data: data
  263. };
  264. switch (data[0] & 0x1f) {
  265. case 0x05:
  266. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  267. break;
  268. case 0x06:
  269. event.nalUnitType = 'sei_rbsp';
  270. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  271. break;
  272. case 0x07:
  273. event.nalUnitType = 'seq_parameter_set_rbsp';
  274. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  275. event.config = readSequenceParameterSet(event.escapedRBSP);
  276. break;
  277. case 0x08:
  278. event.nalUnitType = 'pic_parameter_set_rbsp';
  279. break;
  280. case 0x09:
  281. event.nalUnitType = 'access_unit_delimiter_rbsp';
  282. break;
  283. default:
  284. break;
  285. }
  286. self.trigger('data', event);
  287. });
  288. nalByteStream.on('done', function() {
  289. self.trigger('done');
  290. });
  291. this.flush = function() {
  292. nalByteStream.flush();
  293. };
  294. /**
  295. * Advance the ExpGolomb decoder past a scaling list. The scaling
  296. * list is optionally transmitted as part of a sequence parameter
  297. * set and is not relevant to transmuxing.
  298. * @param count {number} the number of entries in this scaling list
  299. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  300. * start of a scaling list
  301. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  302. */
  303. skipScalingList = function(count, expGolombDecoder) {
  304. var
  305. lastScale = 8,
  306. nextScale = 8,
  307. j,
  308. deltaScale;
  309. for (j = 0; j < count; j++) {
  310. if (nextScale !== 0) {
  311. deltaScale = expGolombDecoder.readExpGolomb();
  312. nextScale = (lastScale + deltaScale + 256) % 256;
  313. }
  314. lastScale = (nextScale === 0) ? lastScale : nextScale;
  315. }
  316. };
  317. /**
  318. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  319. * Sequence Payload"
  320. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  321. * unit
  322. * @return {Uint8Array} the RBSP without any Emulation
  323. * Prevention Bytes
  324. */
  325. discardEmulationPreventionBytes = function(data) {
  326. var
  327. length = data.byteLength,
  328. emulationPreventionBytesPositions = [],
  329. i = 1,
  330. newLength, newData;
  331. // Find all `Emulation Prevention Bytes`
  332. while (i < length - 2) {
  333. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  334. emulationPreventionBytesPositions.push(i + 2);
  335. i += 2;
  336. } else {
  337. i++;
  338. }
  339. }
  340. // If no Emulation Prevention Bytes were found just return the original
  341. // array
  342. if (emulationPreventionBytesPositions.length === 0) {
  343. return data;
  344. }
  345. // Create a new array to hold the NAL unit data
  346. newLength = length - emulationPreventionBytesPositions.length;
  347. newData = new Uint8Array(newLength);
  348. var sourceIndex = 0;
  349. for (i = 0; i < newLength; sourceIndex++, i++) {
  350. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  351. // Skip this byte
  352. sourceIndex++;
  353. // Remove this position index
  354. emulationPreventionBytesPositions.shift();
  355. }
  356. newData[i] = data[sourceIndex];
  357. }
  358. return newData;
  359. };
  360. /**
  361. * Read a sequence parameter set and return some interesting video
  362. * properties. A sequence parameter set is the H264 metadata that
  363. * describes the properties of upcoming video frames.
  364. * @param data {Uint8Array} the bytes of a sequence parameter set
  365. * @return {object} an object with configuration parsed from the
  366. * sequence parameter set, including the dimensions of the
  367. * associated video frames.
  368. */
  369. readSequenceParameterSet = function(data) {
  370. var
  371. frameCropLeftOffset = 0,
  372. frameCropRightOffset = 0,
  373. frameCropTopOffset = 0,
  374. frameCropBottomOffset = 0,
  375. sarScale = 1,
  376. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  377. chromaFormatIdc, picOrderCntType,
  378. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  379. picHeightInMapUnitsMinus1,
  380. frameMbsOnlyFlag,
  381. scalingListCount,
  382. sarRatio,
  383. aspectRatioIdc,
  384. i;
  385. expGolombDecoder = new ExpGolomb(data);
  386. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  387. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  388. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  389. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  390. // some profiles have more optional data we don't need
  391. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  392. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  393. if (chromaFormatIdc === 3) {
  394. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  395. }
  396. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  397. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  398. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  399. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  400. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  401. for (i = 0; i < scalingListCount; i++) {
  402. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  403. if (i < 6) {
  404. skipScalingList(16, expGolombDecoder);
  405. } else {
  406. skipScalingList(64, expGolombDecoder);
  407. }
  408. }
  409. }
  410. }
  411. }
  412. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  413. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  414. if (picOrderCntType === 0) {
  415. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  416. } else if (picOrderCntType === 1) {
  417. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  418. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  419. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  420. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  421. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  422. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  423. }
  424. }
  425. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  426. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  427. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  428. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  429. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  430. if (frameMbsOnlyFlag === 0) {
  431. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  432. }
  433. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  434. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  435. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  436. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  437. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  438. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  439. }
  440. if (expGolombDecoder.readBoolean()) {
  441. // vui_parameters_present_flag
  442. if (expGolombDecoder.readBoolean()) {
  443. // aspect_ratio_info_present_flag
  444. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  445. switch (aspectRatioIdc) {
  446. case 1: sarRatio = [1, 1]; break;
  447. case 2: sarRatio = [12, 11]; break;
  448. case 3: sarRatio = [10, 11]; break;
  449. case 4: sarRatio = [16, 11]; break;
  450. case 5: sarRatio = [40, 33]; break;
  451. case 6: sarRatio = [24, 11]; break;
  452. case 7: sarRatio = [20, 11]; break;
  453. case 8: sarRatio = [32, 11]; break;
  454. case 9: sarRatio = [80, 33]; break;
  455. case 10: sarRatio = [18, 11]; break;
  456. case 11: sarRatio = [15, 11]; break;
  457. case 12: sarRatio = [64, 33]; break;
  458. case 13: sarRatio = [160, 99]; break;
  459. case 14: sarRatio = [4, 3]; break;
  460. case 15: sarRatio = [3, 2]; break;
  461. case 16: sarRatio = [2, 1]; break;
  462. case 255: {
  463. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  464. expGolombDecoder.readUnsignedByte(),
  465. expGolombDecoder.readUnsignedByte() << 8 |
  466. expGolombDecoder.readUnsignedByte() ];
  467. break;
  468. }
  469. }
  470. if (sarRatio) {
  471. sarScale = sarRatio[0] / sarRatio[1];
  472. }
  473. }
  474. }
  475. return {
  476. profileIdc: profileIdc,
  477. levelIdc: levelIdc,
  478. profileCompatibility: profileCompatibility,
  479. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  480. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  481. };
  482. };
  483. };
  484. H264Stream.prototype = new Stream();
  485. module.exports = {
  486. H264Stream: H264Stream,
  487. NalByteStream: NalByteStream
  488. };
  489. },{"../utils/exp-golomb.js":14,"../utils/stream.js":15}],3:[function(require,module,exports){
  490. 'use strict';
  491. var Stream = require('../utils/stream.js');
  492. /**
  493. * The final stage of the transmuxer that emits the flv tags
  494. * for audio, video, and metadata. Also tranlates in time and
  495. * outputs caption data and id3 cues.
  496. */
  497. var CoalesceStream = function(options) {
  498. // Number of Tracks per output segment
  499. // If greater than 1, we combine multiple
  500. // tracks into a single segment
  501. this.numberOfTracks = 0;
  502. this.metadataStream = options.metadataStream;
  503. this.videoTags = [];
  504. this.audioTags = [];
  505. this.videoTrack = null;
  506. this.audioTrack = null;
  507. this.pendingCaptions = [];
  508. this.pendingMetadata = [];
  509. this.pendingTracks = 0;
  510. this.processedTracks = 0;
  511. CoalesceStream.prototype.init.call(this);
  512. // Take output from multiple
  513. this.push = function(output) {
  514. // buffer incoming captions until the associated video segment
  515. // finishes
  516. if (output.text) {
  517. return this.pendingCaptions.push(output);
  518. }
  519. // buffer incoming id3 tags until the final flush
  520. if (output.frames) {
  521. return this.pendingMetadata.push(output);
  522. }
  523. if (output.track.type === 'video') {
  524. this.videoTrack = output.track;
  525. this.videoTags = output.tags;
  526. this.pendingTracks++;
  527. }
  528. if (output.track.type === 'audio') {
  529. this.audioTrack = output.track;
  530. this.audioTags = output.tags;
  531. this.pendingTracks++;
  532. }
  533. };
  534. };
  535. CoalesceStream.prototype = new Stream();
  536. CoalesceStream.prototype.flush = function(flushSource) {
  537. var
  538. id3,
  539. caption,
  540. i,
  541. timelineStartPts,
  542. event = {
  543. tags: {},
  544. captions: [],
  545. captionStreams: {},
  546. metadata: []
  547. };
  548. if (this.pendingTracks < this.numberOfTracks) {
  549. if (flushSource !== 'VideoSegmentStream' &&
  550. flushSource !== 'AudioSegmentStream') {
  551. // Return because we haven't received a flush from a data-generating
  552. // portion of the segment (meaning that we have only recieved meta-data
  553. // or captions.)
  554. return;
  555. } else if (this.pendingTracks === 0) {
  556. // In the case where we receive a flush without any data having been
  557. // received we consider it an emitted track for the purposes of coalescing
  558. // `done` events.
  559. // We do this for the case where there is an audio and video track in the
  560. // segment but no audio data. (seen in several playlists with alternate
  561. // audio tracks and no audio present in the main TS segments.)
  562. this.processedTracks++;
  563. if (this.processedTracks < this.numberOfTracks) {
  564. return;
  565. }
  566. }
  567. }
  568. this.processedTracks += this.pendingTracks;
  569. this.pendingTracks = 0;
  570. if (this.processedTracks < this.numberOfTracks) {
  571. return;
  572. }
  573. if (this.videoTrack) {
  574. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  575. } else if (this.audioTrack) {
  576. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  577. }
  578. event.tags.videoTags = this.videoTags;
  579. event.tags.audioTags = this.audioTags;
  580. // Translate caption PTS times into second offsets into the
  581. // video timeline for the segment, and add track info
  582. for (i = 0; i < this.pendingCaptions.length; i++) {
  583. caption = this.pendingCaptions[i];
  584. caption.startTime = caption.startPts - timelineStartPts;
  585. caption.startTime /= 90e3;
  586. caption.endTime = caption.endPts - timelineStartPts;
  587. caption.endTime /= 90e3;
  588. event.captionStreams[caption.stream] = true;
  589. event.captions.push(caption);
  590. }
  591. // Translate ID3 frame PTS times into second offsets into the
  592. // video timeline for the segment
  593. for (i = 0; i < this.pendingMetadata.length; i++) {
  594. id3 = this.pendingMetadata[i];
  595. id3.cueTime = id3.pts - timelineStartPts;
  596. id3.cueTime /= 90e3;
  597. event.metadata.push(id3);
  598. }
  599. // We add this to every single emitted segment even though we only need
  600. // it for the first
  601. event.metadata.dispatchType = this.metadataStream.dispatchType;
  602. // Reset stream state
  603. this.videoTrack = null;
  604. this.audioTrack = null;
  605. this.videoTags = [];
  606. this.audioTags = [];
  607. this.pendingCaptions.length = 0;
  608. this.pendingMetadata.length = 0;
  609. this.pendingTracks = 0;
  610. this.processedTracks = 0;
  611. // Emit the final segment
  612. this.trigger('data', event);
  613. this.trigger('done');
  614. };
  615. module.exports = CoalesceStream;
  616. },{"../utils/stream.js":15}],4:[function(require,module,exports){
  617. 'use strict';
  618. var FlvTag = require('./flv-tag.js');
  619. // For information on the FLV format, see
  620. // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  621. // Technically, this function returns the header and a metadata FLV tag
  622. // if duration is greater than zero
  623. // duration in seconds
  624. // @return {object} the bytes of the FLV header as a Uint8Array
  625. var getFlvHeader = function(duration, audio, video) { // :ByteArray {
  626. var
  627. headBytes = new Uint8Array(3 + 1 + 1 + 4),
  628. head = new DataView(headBytes.buffer),
  629. metadata,
  630. result,
  631. metadataLength;
  632. // default arguments
  633. duration = duration || 0;
  634. audio = audio === undefined ? true : audio;
  635. video = video === undefined ? true : video;
  636. // signature
  637. head.setUint8(0, 0x46); // 'F'
  638. head.setUint8(1, 0x4c); // 'L'
  639. head.setUint8(2, 0x56); // 'V'
  640. // version
  641. head.setUint8(3, 0x01);
  642. // flags
  643. head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
  644. // data offset, should be 9 for FLV v1
  645. head.setUint32(5, headBytes.byteLength);
  646. // init the first FLV tag
  647. if (duration <= 0) {
  648. // no duration available so just write the first field of the first
  649. // FLV tag
  650. result = new Uint8Array(headBytes.byteLength + 4);
  651. result.set(headBytes);
  652. result.set([0, 0, 0, 0], headBytes.byteLength);
  653. return result;
  654. }
  655. // write out the duration metadata tag
  656. metadata = new FlvTag(FlvTag.METADATA_TAG);
  657. metadata.pts = metadata.dts = 0;
  658. metadata.writeMetaDataDouble('duration', duration);
  659. metadataLength = metadata.finalize().length;
  660. result = new Uint8Array(headBytes.byteLength + metadataLength);
  661. result.set(headBytes);
  662. result.set(head.byteLength, metadataLength);
  663. return result;
  664. };
  665. module.exports = getFlvHeader;
  666. },{"./flv-tag.js":5}],5:[function(require,module,exports){
  667. /**
  668. * An object that stores the bytes of an FLV tag and methods for
  669. * querying and manipulating that data.
  670. * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
  671. */
  672. 'use strict';
  673. var FlvTag;
  674. // (type:uint, extraData:Boolean = false) extends ByteArray
  675. FlvTag = function(type, extraData) {
  676. var
  677. // Counter if this is a metadata tag, nal start marker if this is a video
  678. // tag. unused if this is an audio tag
  679. adHoc = 0, // :uint
  680. // The default size is 16kb but this is not enough to hold iframe
  681. // data and the resizing algorithm costs a bit so we create a larger
  682. // starting buffer for video tags
  683. bufferStartSize = 16384,
  684. // checks whether the FLV tag has enough capacity to accept the proposed
  685. // write and re-allocates the internal buffers if necessary
  686. prepareWrite = function(flv, count) {
  687. var
  688. bytes,
  689. minLength = flv.position + count;
  690. if (minLength < flv.bytes.byteLength) {
  691. // there's enough capacity so do nothing
  692. return;
  693. }
  694. // allocate a new buffer and copy over the data that will not be modified
  695. bytes = new Uint8Array(minLength * 2);
  696. bytes.set(flv.bytes.subarray(0, flv.position), 0);
  697. flv.bytes = bytes;
  698. flv.view = new DataView(flv.bytes.buffer);
  699. },
  700. // commonly used metadata properties
  701. widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
  702. heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
  703. videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
  704. i;
  705. if (!FlvTag.widthBytes) {
  706. // calculating the bytes of common metadata names ahead of time makes the
  707. // corresponding writes faster because we don't have to loop over the
  708. // characters
  709. // re-test with test/perf.html if you're planning on changing this
  710. for (i = 0; i < 'width'.length; i++) {
  711. widthBytes[i] = 'width'.charCodeAt(i);
  712. }
  713. for (i = 0; i < 'height'.length; i++) {
  714. heightBytes[i] = 'height'.charCodeAt(i);
  715. }
  716. for (i = 0; i < 'videocodecid'.length; i++) {
  717. videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
  718. }
  719. FlvTag.widthBytes = widthBytes;
  720. FlvTag.heightBytes = heightBytes;
  721. FlvTag.videocodecidBytes = videocodecidBytes;
  722. }
  723. this.keyFrame = false; // :Boolean
  724. switch (type) {
  725. case FlvTag.VIDEO_TAG:
  726. this.length = 16;
  727. // Start the buffer at 256k
  728. bufferStartSize *= 6;
  729. break;
  730. case FlvTag.AUDIO_TAG:
  731. this.length = 13;
  732. this.keyFrame = true;
  733. break;
  734. case FlvTag.METADATA_TAG:
  735. this.length = 29;
  736. this.keyFrame = true;
  737. break;
  738. default:
  739. throw new Error('Unknown FLV tag type');
  740. }
  741. this.bytes = new Uint8Array(bufferStartSize);
  742. this.view = new DataView(this.bytes.buffer);
  743. this.bytes[0] = type;
  744. this.position = this.length;
  745. this.keyFrame = extraData; // Defaults to false
  746. // presentation timestamp
  747. this.pts = 0;
  748. // decoder timestamp
  749. this.dts = 0;
  750. // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  751. this.writeBytes = function(bytes, offset, length) {
  752. var
  753. start = offset || 0,
  754. end;
  755. length = length || bytes.byteLength;
  756. end = start + length;
  757. prepareWrite(this, length);
  758. this.bytes.set(bytes.subarray(start, end), this.position);
  759. this.position += length;
  760. this.length = Math.max(this.length, this.position);
  761. };
  762. // ByteArray#writeByte(value:int):void
  763. this.writeByte = function(byte) {
  764. prepareWrite(this, 1);
  765. this.bytes[this.position] = byte;
  766. this.position++;
  767. this.length = Math.max(this.length, this.position);
  768. };
  769. // ByteArray#writeShort(value:int):void
  770. this.writeShort = function(short) {
  771. prepareWrite(this, 2);
  772. this.view.setUint16(this.position, short);
  773. this.position += 2;
  774. this.length = Math.max(this.length, this.position);
  775. };
  776. // Negative index into array
  777. // (pos:uint):int
  778. this.negIndex = function(pos) {
  779. return this.bytes[this.length - pos];
  780. };
  781. // The functions below ONLY work when this[0] == VIDEO_TAG.
  782. // We are not going to check for that because we dont want the overhead
  783. // (nal:ByteArray = null):int
  784. this.nalUnitSize = function() {
  785. if (adHoc === 0) {
  786. return 0;
  787. }
  788. return this.length - (adHoc + 4);
  789. };
  790. this.startNalUnit = function() {
  791. // remember position and add 4 bytes
  792. if (adHoc > 0) {
  793. throw new Error('Attempted to create new NAL wihout closing the old one');
  794. }
  795. // reserve 4 bytes for nal unit size
  796. adHoc = this.length;
  797. this.length += 4;
  798. this.position = this.length;
  799. };
  800. // (nal:ByteArray = null):void
  801. this.endNalUnit = function(nalContainer) {
  802. var
  803. nalStart, // :uint
  804. nalLength; // :uint
  805. // Rewind to the marker and write the size
  806. if (this.length === adHoc + 4) {
  807. // we started a nal unit, but didnt write one, so roll back the 4 byte size value
  808. this.length -= 4;
  809. } else if (adHoc > 0) {
  810. nalStart = adHoc + 4;
  811. nalLength = this.length - nalStart;
  812. this.position = adHoc;
  813. this.view.setUint32(this.position, nalLength);
  814. this.position = this.length;
  815. if (nalContainer) {
  816. // Add the tag to the NAL unit
  817. nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
  818. }
  819. }
  820. adHoc = 0;
  821. };
  822. /**
  823. * Write out a 64-bit floating point valued metadata property. This method is
  824. * called frequently during a typical parse and needs to be fast.
  825. */
  826. // (key:String, val:Number):void
  827. this.writeMetaDataDouble = function(key, val) {
  828. var i;
  829. prepareWrite(this, 2 + key.length + 9);
  830. // write size of property name
  831. this.view.setUint16(this.position, key.length);
  832. this.position += 2;
  833. // this next part looks terrible but it improves parser throughput by
  834. // 10kB/s in my testing
  835. // write property name
  836. if (key === 'width') {
  837. this.bytes.set(widthBytes, this.position);
  838. this.position += 5;
  839. } else if (key === 'height') {
  840. this.bytes.set(heightBytes, this.position);
  841. this.position += 6;
  842. } else if (key === 'videocodecid') {
  843. this.bytes.set(videocodecidBytes, this.position);
  844. this.position += 12;
  845. } else {
  846. for (i = 0; i < key.length; i++) {
  847. this.bytes[this.position] = key.charCodeAt(i);
  848. this.position++;
  849. }
  850. }
  851. // skip null byte
  852. this.position++;
  853. // write property value
  854. this.view.setFloat64(this.position, val);
  855. this.position += 8;
  856. // update flv tag length
  857. this.length = Math.max(this.length, this.position);
  858. ++adHoc;
  859. };
  860. // (key:String, val:Boolean):void
  861. this.writeMetaDataBoolean = function(key, val) {
  862. var i;
  863. prepareWrite(this, 2);
  864. this.view.setUint16(this.position, key.length);
  865. this.position += 2;
  866. for (i = 0; i < key.length; i++) {
  867. // if key.charCodeAt(i) >= 255, handle error
  868. prepareWrite(this, 1);
  869. this.bytes[this.position] = key.charCodeAt(i);
  870. this.position++;
  871. }
  872. prepareWrite(this, 2);
  873. this.view.setUint8(this.position, 0x01);
  874. this.position++;
  875. this.view.setUint8(this.position, val ? 0x01 : 0x00);
  876. this.position++;
  877. this.length = Math.max(this.length, this.position);
  878. ++adHoc;
  879. };
  880. // ():ByteArray
  881. this.finalize = function() {
  882. var
  883. dtsDelta, // :int
  884. len; // :int
  885. switch (this.bytes[0]) {
  886. // Video Data
  887. case FlvTag.VIDEO_TAG:
  888. // We only support AVC, 1 = key frame (for AVC, a seekable
  889. // frame), 2 = inter frame (for AVC, a non-seekable frame)
  890. this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
  891. this.bytes[12] = extraData ? 0x00 : 0x01;
  892. dtsDelta = this.pts - this.dts;
  893. this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
  894. this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
  895. this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
  896. break;
  897. case FlvTag.AUDIO_TAG:
  898. this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
  899. this.bytes[12] = extraData ? 0x00 : 0x01;
  900. break;
  901. case FlvTag.METADATA_TAG:
  902. this.position = 11;
  903. this.view.setUint8(this.position, 0x02); // String type
  904. this.position++;
  905. this.view.setUint16(this.position, 0x0A); // 10 Bytes
  906. this.position += 2;
  907. // set "onMetaData"
  908. this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
  909. 0x74, 0x61, 0x44, 0x61,
  910. 0x74, 0x61], this.position);
  911. this.position += 10;
  912. this.bytes[this.position] = 0x08; // Array type
  913. this.position++;
  914. this.view.setUint32(this.position, adHoc);
  915. this.position = this.length;
  916. this.bytes.set([0, 0, 9], this.position);
  917. this.position += 3; // End Data Tag
  918. this.length = this.position;
  919. break;
  920. }
  921. len = this.length - 11;
  922. // write the DataSize field
  923. this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
  924. this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
  925. this.bytes[ 3] = (len & 0x000000FF) >>> 0;
  926. // write the Timestamp
  927. this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
  928. this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8;
  929. this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0;
  930. this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
  931. // write the StreamID
  932. this.bytes[ 8] = 0;
  933. this.bytes[ 9] = 0;
  934. this.bytes[10] = 0;
  935. // Sometimes we're at the end of the view and have one slot to write a
  936. // uint32, so, prepareWrite of count 4, since, view is uint8
  937. prepareWrite(this, 4);
  938. this.view.setUint32(this.length, this.length);
  939. this.length += 4;
  940. this.position += 4;
  941. // trim down the byte buffer to what is actually being used
  942. this.bytes = this.bytes.subarray(0, this.length);
  943. this.frameTime = FlvTag.frameTime(this.bytes);
  944. // if bytes.bytelength isn't equal to this.length, handle error
  945. return this;
  946. };
  947. };
  948. FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
  949. FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
  950. FlvTag.METADATA_TAG = 0x12; // == 18, :uint
  951. // (tag:ByteArray):Boolean {
  952. FlvTag.isAudioFrame = function(tag) {
  953. return FlvTag.AUDIO_TAG === tag[0];
  954. };
  955. // (tag:ByteArray):Boolean {
  956. FlvTag.isVideoFrame = function(tag) {
  957. return FlvTag.VIDEO_TAG === tag[0];
  958. };
  959. // (tag:ByteArray):Boolean {
  960. FlvTag.isMetaData = function(tag) {
  961. return FlvTag.METADATA_TAG === tag[0];
  962. };
  963. // (tag:ByteArray):Boolean {
  964. FlvTag.isKeyFrame = function(tag) {
  965. if (FlvTag.isVideoFrame(tag)) {
  966. return tag[11] === 0x17;
  967. }
  968. if (FlvTag.isAudioFrame(tag)) {
  969. return true;
  970. }
  971. if (FlvTag.isMetaData(tag)) {
  972. return true;
  973. }
  974. return false;
  975. };
  976. // (tag:ByteArray):uint {
  977. FlvTag.frameTime = function(tag) {
  978. var pts = tag[ 4] << 16; // :uint
  979. pts |= tag[ 5] << 8;
  980. pts |= tag[ 6] << 0;
  981. pts |= tag[ 7] << 24;
  982. return pts;
  983. };
  984. module.exports = FlvTag;
  985. },{}],6:[function(require,module,exports){
  986. module.exports = {
  987. tag: require('./flv-tag'),
  988. Transmuxer: require('./transmuxer'),
  989. getFlvHeader: require('./flv-header')
  990. };
  991. },{"./flv-header":4,"./flv-tag":5,"./transmuxer":8}],7:[function(require,module,exports){
  992. 'use strict';
  993. var TagList = function() {
  994. var self = this;
  995. this.list = [];
  996. this.push = function(tag) {
  997. this.list.push({
  998. bytes: tag.bytes,
  999. dts: tag.dts,
  1000. pts: tag.pts,
  1001. keyFrame: tag.keyFrame,
  1002. metaDataTag: tag.metaDataTag
  1003. });
  1004. };
  1005. Object.defineProperty(this, 'length', {
  1006. get: function() {
  1007. return self.list.length;
  1008. }
  1009. });
  1010. };
  1011. module.exports = TagList;
  1012. },{}],8:[function(require,module,exports){
  1013. 'use strict';
  1014. var Stream = require('../utils/stream.js');
  1015. var FlvTag = require('./flv-tag.js');
  1016. var m2ts = require('../m2ts/m2ts.js');
  1017. var AdtsStream = require('../codecs/adts.js');
  1018. var H264Stream = require('../codecs/h264').H264Stream;
  1019. var CoalesceStream = require('./coalesce-stream.js');
  1020. var TagList = require('./tag-list.js');
  1021. var
  1022. Transmuxer,
  1023. VideoSegmentStream,
  1024. AudioSegmentStream,
  1025. collectTimelineInfo,
  1026. metaDataTag,
  1027. extraDataTag;
  1028. /**
  1029. * Store information about the start and end of the tracka and the
  1030. * duration for each frame/sample we process in order to calculate
  1031. * the baseMediaDecodeTime
  1032. */
  1033. collectTimelineInfo = function(track, data) {
  1034. if (typeof data.pts === 'number') {
  1035. if (track.timelineStartInfo.pts === undefined) {
  1036. track.timelineStartInfo.pts = data.pts;
  1037. } else {
  1038. track.timelineStartInfo.pts =
  1039. Math.min(track.timelineStartInfo.pts, data.pts);
  1040. }
  1041. }
  1042. if (typeof data.dts === 'number') {
  1043. if (track.timelineStartInfo.dts === undefined) {
  1044. track.timelineStartInfo.dts = data.dts;
  1045. } else {
  1046. track.timelineStartInfo.dts =
  1047. Math.min(track.timelineStartInfo.dts, data.dts);
  1048. }
  1049. }
  1050. };
  1051. metaDataTag = function(track, pts) {
  1052. var
  1053. tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
  1054. tag.dts = pts;
  1055. tag.pts = pts;
  1056. tag.writeMetaDataDouble('videocodecid', 7);
  1057. tag.writeMetaDataDouble('width', track.width);
  1058. tag.writeMetaDataDouble('height', track.height);
  1059. return tag;
  1060. };
  1061. extraDataTag = function(track, pts) {
  1062. var
  1063. i,
  1064. tag = new FlvTag(FlvTag.VIDEO_TAG, true);
  1065. tag.dts = pts;
  1066. tag.pts = pts;
  1067. tag.writeByte(0x01);// version
  1068. tag.writeByte(track.profileIdc);// profile
  1069. tag.writeByte(track.profileCompatibility);// compatibility
  1070. tag.writeByte(track.levelIdc);// level
  1071. tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  1072. tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
  1073. tag.writeShort(track.sps[0].length); // data of SPS
  1074. tag.writeBytes(track.sps[0]); // SPS
  1075. tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  1076. for (i = 0; i < track.pps.length; ++i) {
  1077. tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
  1078. tag.writeBytes(track.pps[i]); // data of PPS
  1079. }
  1080. return tag;
  1081. };
  1082. /**
  1083. * Constructs a single-track, media segment from AAC data
  1084. * events. The output of this stream can be fed to flash.
  1085. */
  1086. AudioSegmentStream = function(track) {
  1087. var
  1088. adtsFrames = [],
  1089. videoKeyFrames = [],
  1090. oldExtraData;
  1091. AudioSegmentStream.prototype.init.call(this);
  1092. this.push = function(data) {
  1093. collectTimelineInfo(track, data);
  1094. if (track) {
  1095. track.audioobjecttype = data.audioobjecttype;
  1096. track.channelcount = data.channelcount;
  1097. track.samplerate = data.samplerate;
  1098. track.samplingfrequencyindex = data.samplingfrequencyindex;
  1099. track.samplesize = data.samplesize;
  1100. track.extraData = (track.audioobjecttype << 11) |
  1101. (track.samplingfrequencyindex << 7) |
  1102. (track.channelcount << 3);
  1103. }
  1104. data.pts = Math.round(data.pts / 90);
  1105. data.dts = Math.round(data.dts / 90);
  1106. // buffer audio data until end() is called
  1107. adtsFrames.push(data);
  1108. };
  1109. this.flush = function() {
  1110. var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
  1111. // return early if no audio data has been observed
  1112. if (adtsFrames.length === 0) {
  1113. this.trigger('done', 'AudioSegmentStream');
  1114. return;
  1115. }
  1116. lastMetaPts = -Infinity;
  1117. while (adtsFrames.length) {
  1118. currentFrame = adtsFrames.shift();
  1119. // write out a metadata frame at every video key frame
  1120. if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) {
  1121. lastMetaPts = videoKeyFrames.shift();
  1122. this.writeMetaDataTags(tags, lastMetaPts);
  1123. }
  1124. // also write out metadata tags every 1 second so that the decoder
  1125. // is re-initialized quickly after seeking into a different
  1126. // audio configuration.
  1127. if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
  1128. this.writeMetaDataTags(tags, currentFrame.pts);
  1129. oldExtraData = track.extraData;
  1130. lastMetaPts = currentFrame.pts;
  1131. }
  1132. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
  1133. adtsFrame.pts = currentFrame.pts;
  1134. adtsFrame.dts = currentFrame.dts;
  1135. adtsFrame.writeBytes(currentFrame.data);
  1136. tags.push(adtsFrame.finalize());
  1137. }
  1138. videoKeyFrames.length = 0;
  1139. oldExtraData = null;
  1140. this.trigger('data', {track: track, tags: tags.list});
  1141. this.trigger('done', 'AudioSegmentStream');
  1142. };
  1143. this.writeMetaDataTags = function(tags, pts) {
  1144. var adtsFrame;
  1145. adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
  1146. // For audio, DTS is always the same as PTS. We want to set the DTS
  1147. // however so we can compare with video DTS to determine approximate
  1148. // packet order
  1149. adtsFrame.pts = pts;
  1150. adtsFrame.dts = pts;
  1151. // AAC is always 10
  1152. adtsFrame.writeMetaDataDouble('audiocodecid', 10);
  1153. adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
  1154. adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
  1155. // Is AAC always 16 bit?
  1156. adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
  1157. tags.push(adtsFrame.finalize());
  1158. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
  1159. // For audio, DTS is always the same as PTS. We want to set the DTS
  1160. // however so we can compare with video DTS to determine approximate
  1161. // packet order
  1162. adtsFrame.pts = pts;
  1163. adtsFrame.dts = pts;
  1164. adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
  1165. adtsFrame.position += 2;
  1166. adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
  1167. tags.push(adtsFrame.finalize());
  1168. };
  1169. this.onVideoKeyFrame = function(pts) {
  1170. videoKeyFrames.push(pts);
  1171. };
  1172. };
  1173. AudioSegmentStream.prototype = new Stream();
  1174. /**
  1175. * Store FlvTags for the h264 stream
  1176. * @param track {object} track metadata configuration
  1177. */
  1178. VideoSegmentStream = function(track) {
  1179. var
  1180. nalUnits = [],
  1181. config,
  1182. h264Frame;
  1183. VideoSegmentStream.prototype.init.call(this);
  1184. this.finishFrame = function(tags, frame) {
  1185. if (!frame) {
  1186. return;
  1187. }
  1188. // Check if keyframe and the length of tags.
  1189. // This makes sure we write metadata on the first frame of a segment.
  1190. if (config && track && track.newMetadata &&
  1191. (frame.keyFrame || tags.length === 0)) {
  1192. // Push extra data on every IDR frame in case we did a stream change + seek
  1193. var metaTag = metaDataTag(config, frame.dts).finalize();
  1194. var extraTag = extraDataTag(track, frame.dts).finalize();
  1195. metaTag.metaDataTag = extraTag.metaDataTag = true;
  1196. tags.push(metaTag);
  1197. tags.push(extraTag);
  1198. track.newMetadata = false;
  1199. this.trigger('keyframe', frame.dts);
  1200. }
  1201. frame.endNalUnit();
  1202. tags.push(frame.finalize());
  1203. h264Frame = null;
  1204. };
  1205. this.push = function(data) {
  1206. collectTimelineInfo(track, data);
  1207. data.pts = Math.round(data.pts / 90);
  1208. data.dts = Math.round(data.dts / 90);
  1209. // buffer video until flush() is called
  1210. nalUnits.push(data);
  1211. };
  1212. this.flush = function() {
  1213. var
  1214. currentNal,
  1215. tags = new TagList();
  1216. // Throw away nalUnits at the start of the byte stream until we find
  1217. // the first AUD
  1218. while (nalUnits.length) {
  1219. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  1220. break;
  1221. }
  1222. nalUnits.shift();
  1223. }
  1224. // return early if no video data has been observed
  1225. if (nalUnits.length === 0) {
  1226. this.trigger('done', 'VideoSegmentStream');
  1227. return;
  1228. }
  1229. while (nalUnits.length) {
  1230. currentNal = nalUnits.shift();
  1231. // record the track config
  1232. if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
  1233. track.newMetadata = true;
  1234. config = currentNal.config;
  1235. track.width = config.width;
  1236. track.height = config.height;
  1237. track.sps = [currentNal.data];
  1238. track.profileIdc = config.profileIdc;
  1239. track.levelIdc = config.levelIdc;
  1240. track.profileCompatibility = config.profileCompatibility;
  1241. h264Frame.endNalUnit();
  1242. } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
  1243. track.newMetadata = true;
  1244. track.pps = [currentNal.data];
  1245. h264Frame.endNalUnit();
  1246. } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  1247. if (h264Frame) {
  1248. this.finishFrame(tags, h264Frame);
  1249. }
  1250. h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
  1251. h264Frame.pts = currentNal.pts;
  1252. h264Frame.dts = currentNal.dts;
  1253. } else {
  1254. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  1255. // the current sample is a key frame
  1256. h264Frame.keyFrame = true;
  1257. }
  1258. h264Frame.endNalUnit();
  1259. }
  1260. h264Frame.startNalUnit();
  1261. h264Frame.writeBytes(currentNal.data);
  1262. }
  1263. if (h264Frame) {
  1264. this.finishFrame(tags, h264Frame);
  1265. }
  1266. this.trigger('data', {track: track, tags: tags.list});
  1267. // Continue with the flush process now
  1268. this.trigger('done', 'VideoSegmentStream');
  1269. };
  1270. };
  1271. VideoSegmentStream.prototype = new Stream();
  1272. /**
  1273. * An object that incrementally transmuxes MPEG2 Trasport Stream
  1274. * chunks into an FLV.
  1275. */
  1276. Transmuxer = function(options) {
  1277. var
  1278. self = this,
  1279. packetStream, parseStream, elementaryStream,
  1280. videoTimestampRolloverStream, audioTimestampRolloverStream,
  1281. timedMetadataTimestampRolloverStream,
  1282. adtsStream, h264Stream,
  1283. videoSegmentStream, audioSegmentStream, captionStream,
  1284. coalesceStream;
  1285. Transmuxer.prototype.init.call(this);
  1286. options = options || {};
  1287. // expose the metadata stream
  1288. this.metadataStream = new m2ts.MetadataStream();
  1289. options.metadataStream = this.metadataStream;
  1290. // set up the parsing pipeline
  1291. packetStream = new m2ts.TransportPacketStream();
  1292. parseStream = new m2ts.TransportParseStream();
  1293. elementaryStream = new m2ts.ElementaryStream();
  1294. videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  1295. audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  1296. timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  1297. adtsStream = new AdtsStream();
  1298. h264Stream = new H264Stream();
  1299. coalesceStream = new CoalesceStream(options);
  1300. // disassemble MPEG2-TS packets into elementary streams
  1301. packetStream
  1302. .pipe(parseStream)
  1303. .pipe(elementaryStream);
  1304. // !!THIS ORDER IS IMPORTANT!!
  1305. // demux the streams
  1306. elementaryStream
  1307. .pipe(videoTimestampRolloverStream)
  1308. .pipe(h264Stream);
  1309. elementaryStream
  1310. .pipe(audioTimestampRolloverStream)
  1311. .pipe(adtsStream);
  1312. elementaryStream
  1313. .pipe(timedMetadataTimestampRolloverStream)
  1314. .pipe(this.metadataStream)
  1315. .pipe(coalesceStream);
  1316. // if CEA-708 parsing is available, hook up a caption stream
  1317. captionStream = new m2ts.CaptionStream();
  1318. h264Stream.pipe(captionStream)
  1319. .pipe(coalesceStream);
  1320. // hook up the segment streams once track metadata is delivered
  1321. elementaryStream.on('data', function(data) {
  1322. var i, videoTrack, audioTrack;
  1323. if (data.type === 'metadata') {
  1324. i = data.tracks.length;
  1325. // scan the tracks listed in the metadata
  1326. while (i--) {
  1327. if (data.tracks[i].type === 'video') {
  1328. videoTrack = data.tracks[i];
  1329. } else if (data.tracks[i].type === 'audio') {
  1330. audioTrack = data.tracks[i];
  1331. }
  1332. }
  1333. // hook up the video segment stream to the first track with h264 data
  1334. if (videoTrack && !videoSegmentStream) {
  1335. coalesceStream.numberOfTracks++;
  1336. videoSegmentStream = new VideoSegmentStream(videoTrack);
  1337. // Set up the final part of the video pipeline
  1338. h264Stream
  1339. .pipe(videoSegmentStream)
  1340. .pipe(coalesceStream);
  1341. }
  1342. if (audioTrack && !audioSegmentStream) {
  1343. // hook up the audio segment stream to the first track with aac data
  1344. coalesceStream.numberOfTracks++;
  1345. audioSegmentStream = new AudioSegmentStream(audioTrack);
  1346. // Set up the final part of the audio pipeline
  1347. adtsStream
  1348. .pipe(audioSegmentStream)
  1349. .pipe(coalesceStream);
  1350. if (videoSegmentStream) {
  1351. videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame);
  1352. }
  1353. }
  1354. }
  1355. });
  1356. // feed incoming data to the front of the parsing pipeline
  1357. this.push = function(data) {
  1358. packetStream.push(data);
  1359. };
  1360. // flush any buffered data
  1361. this.flush = function() {
  1362. // Start at the top of the pipeline and flush all pending work
  1363. packetStream.flush();
  1364. };
  1365. // Caption data has to be reset when seeking outside buffered range
  1366. this.resetCaptions = function() {
  1367. captionStream.reset();
  1368. };
  1369. // Re-emit any data coming from the coalesce stream to the outside world
  1370. coalesceStream.on('data', function(event) {
  1371. self.trigger('data', event);
  1372. });
  1373. // Let the consumer know we have finished flushing the entire pipeline
  1374. coalesceStream.on('done', function() {
  1375. self.trigger('done');
  1376. });
  1377. };
  1378. Transmuxer.prototype = new Stream();
  1379. // forward compatibility
  1380. module.exports = Transmuxer;
  1381. },{"../codecs/adts.js":1,"../codecs/h264":2,"../m2ts/m2ts.js":10,"../utils/stream.js":15,"./coalesce-stream.js":3,"./flv-tag.js":5,"./tag-list.js":7}],9:[function(require,module,exports){
  1382. /**
  1383. * mux.js
  1384. *
  1385. * Copyright (c) 2015 Brightcove
  1386. * All rights reserved.
  1387. *
  1388. * Reads in-band caption information from a video elementary
  1389. * stream. Captions must follow the CEA-708 standard for injection
  1390. * into an MPEG-2 transport streams.
  1391. * @see https://en.wikipedia.org/wiki/CEA-708
  1392. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  1393. */
  1394. 'use strict';
  1395. // -----------------
  1396. // Link To Transport
  1397. // -----------------
  1398. // Supplemental enhancement information (SEI) NAL units have a
  1399. // payload type field to indicate how they are to be
  1400. // interpreted. CEAS-708 caption content is always transmitted with
  1401. // payload type 0x04.
  1402. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  1403. RBSP_TRAILING_BITS = 128,
  1404. Stream = require('../utils/stream');
  1405. /**
  1406. * Parse a supplemental enhancement information (SEI) NAL unit.
  1407. * Stops parsing once a message of type ITU T T35 has been found.
  1408. *
  1409. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  1410. * @return {object} the parsed SEI payload
  1411. * @see Rec. ITU-T H.264, 7.3.2.3.1
  1412. */
  1413. var parseSei = function(bytes) {
  1414. var
  1415. i = 0,
  1416. result = {
  1417. payloadType: -1,
  1418. payloadSize: 0
  1419. },
  1420. payloadType = 0,
  1421. payloadSize = 0;
  1422. // go through the sei_rbsp parsing each each individual sei_message
  1423. while (i < bytes.byteLength) {
  1424. // stop once we have hit the end of the sei_rbsp
  1425. if (bytes[i] === RBSP_TRAILING_BITS) {
  1426. break;
  1427. }
  1428. // Parse payload type
  1429. while (bytes[i] === 0xFF) {
  1430. payloadType += 255;
  1431. i++;
  1432. }
  1433. payloadType += bytes[i++];
  1434. // Parse payload size
  1435. while (bytes[i] === 0xFF) {
  1436. payloadSize += 255;
  1437. i++;
  1438. }
  1439. payloadSize += bytes[i++];
  1440. // this sei_message is a 608/708 caption so save it and break
  1441. // there can only ever be one caption message in a frame's sei
  1442. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  1443. result.payloadType = payloadType;
  1444. result.payloadSize = payloadSize;
  1445. result.payload = bytes.subarray(i, i + payloadSize);
  1446. break;
  1447. }
  1448. // skip the payload and parse the next message
  1449. i += payloadSize;
  1450. payloadType = 0;
  1451. payloadSize = 0;
  1452. }
  1453. return result;
  1454. };
  1455. // see ANSI/SCTE 128-1 (2013), section 8.1
  1456. var parseUserData = function(sei) {
  1457. // itu_t_t35_contry_code must be 181 (United States) for
  1458. // captions
  1459. if (sei.payload[0] !== 181) {
  1460. return null;
  1461. }
  1462. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  1463. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  1464. return null;
  1465. }
  1466. // the user_identifier should be "GA94" to indicate ATSC1 data
  1467. if (String.fromCharCode(sei.payload[3],
  1468. sei.payload[4],
  1469. sei.payload[5],
  1470. sei.payload[6]) !== 'GA94') {
  1471. return null;
  1472. }
  1473. // finally, user_data_type_code should be 0x03 for caption data
  1474. if (sei.payload[7] !== 0x03) {
  1475. return null;
  1476. }
  1477. // return the user_data_type_structure and strip the trailing
  1478. // marker bits
  1479. return sei.payload.subarray(8, sei.payload.length - 1);
  1480. };
  1481. // see CEA-708-D, section 4.4
  1482. var parseCaptionPackets = function(pts, userData) {
  1483. var results = [], i, count, offset, data;
  1484. // if this is just filler, return immediately
  1485. if (!(userData[0] & 0x40)) {
  1486. return results;
  1487. }
  1488. // parse out the cc_data_1 and cc_data_2 fields
  1489. count = userData[0] & 0x1f;
  1490. for (i = 0; i < count; i++) {
  1491. offset = i * 3;
  1492. data = {
  1493. type: userData[offset + 2] & 0x03,
  1494. pts: pts
  1495. };
  1496. // capture cc data when cc_valid is 1
  1497. if (userData[offset + 2] & 0x04) {
  1498. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  1499. results.push(data);
  1500. }
  1501. }
  1502. return results;
  1503. };
  1504. var CaptionStream = function() {
  1505. CaptionStream.prototype.init.call(this);
  1506. this.captionPackets_ = [];
  1507. this.ccStreams_ = [
  1508. new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
  1509. new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
  1510. new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
  1511. new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
  1512. ];
  1513. this.reset();
  1514. // forward data and done events from CCs to this CaptionStream
  1515. this.ccStreams_.forEach(function(cc) {
  1516. cc.on('data', this.trigger.bind(this, 'data'));
  1517. cc.on('done', this.trigger.bind(this, 'done'));
  1518. }, this);
  1519. };
  1520. CaptionStream.prototype = new Stream();
  1521. CaptionStream.prototype.push = function(event) {
  1522. var sei, userData;
  1523. // only examine SEI NALs
  1524. if (event.nalUnitType !== 'sei_rbsp') {
  1525. return;
  1526. }
  1527. // parse the sei
  1528. sei = parseSei(event.escapedRBSP);
  1529. // ignore everything but user_data_registered_itu_t_t35
  1530. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  1531. return;
  1532. }
  1533. // parse out the user data payload
  1534. userData = parseUserData(sei);
  1535. // ignore unrecognized userData
  1536. if (!userData) {
  1537. return;
  1538. }
  1539. // Sometimes, the same segment # will be downloaded twice. To stop the
  1540. // caption data from being processed twice, we track the latest dts we've
  1541. // received and ignore everything with a dts before that. However, since
  1542. // data for a specific dts can be split across 2 packets on either side of
  1543. // a segment boundary, we need to make sure we *don't* ignore the second
  1544. // dts packet we receive that has dts === this.latestDts_. And thus, the
  1545. // ignoreNextEqualDts_ flag was born.
  1546. if (event.dts < this.latestDts_) {
  1547. // We've started getting older data, so set the flag.
  1548. this.ignoreNextEqualDts_ = true;
  1549. return;
  1550. } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
  1551. // We've received the last duplicate packet, time to start processing again
  1552. this.ignoreNextEqualDts_ = false;
  1553. return;
  1554. }
  1555. // parse out CC data packets and save them for later
  1556. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  1557. this.latestDts_ = event.dts;
  1558. };
  1559. CaptionStream.prototype.flush = function() {
  1560. // make sure we actually parsed captions before proceeding
  1561. if (!this.captionPackets_.length) {
  1562. this.ccStreams_.forEach(function(cc) {
  1563. cc.flush();
  1564. }, this);
  1565. return;
  1566. }
  1567. // In Chrome, the Array#sort function is not stable so add a
  1568. // presortIndex that we can use to ensure we get a stable-sort
  1569. this.captionPackets_.forEach(function(elem, idx) {
  1570. elem.presortIndex = idx;
  1571. });
  1572. // sort caption byte-pairs based on their PTS values
  1573. this.captionPackets_.sort(function(a, b) {
  1574. if (a.pts === b.pts) {
  1575. return a.presortIndex - b.presortIndex;
  1576. }
  1577. return a.pts - b.pts;
  1578. });
  1579. this.captionPackets_.forEach(function(packet) {
  1580. if (packet.type < 2) {
  1581. // Dispatch packet to the right Cea608Stream
  1582. this.dispatchCea608Packet(packet);
  1583. }
  1584. // this is where an 'else' would go for a dispatching packets
  1585. // to a theoretical Cea708Stream that handles SERVICEn data
  1586. }, this);
  1587. this.captionPackets_.length = 0;
  1588. this.ccStreams_.forEach(function(cc) {
  1589. cc.flush();
  1590. }, this);
  1591. return;
  1592. };
  1593. CaptionStream.prototype.reset = function() {
  1594. this.latestDts_ = null;
  1595. this.ignoreNextEqualDts_ = false;
  1596. this.activeCea608Channel_ = [null, null];
  1597. this.ccStreams_.forEach(function(ccStream) {
  1598. ccStream.reset();
  1599. });
  1600. };
  1601. CaptionStream.prototype.dispatchCea608Packet = function(packet) {
  1602. // NOTE: packet.type is the CEA608 field
  1603. if (this.setsChannel1Active(packet)) {
  1604. this.activeCea608Channel_[packet.type] = 0;
  1605. } else if (this.setsChannel2Active(packet)) {
  1606. this.activeCea608Channel_[packet.type] = 1;
  1607. }
  1608. if (this.activeCea608Channel_[packet.type] === null) {
  1609. // If we haven't received anything to set the active channel, discard the
  1610. // data; we don't want jumbled captions
  1611. return;
  1612. }
  1613. this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
  1614. };
  1615. CaptionStream.prototype.setsChannel1Active = function(packet) {
  1616. return ((packet.ccData & 0x7800) === 0x1000);
  1617. };
  1618. CaptionStream.prototype.setsChannel2Active = function(packet) {
  1619. return ((packet.ccData & 0x7800) === 0x1800);
  1620. };
  1621. // ----------------------
  1622. // Session to Application
  1623. // ----------------------
  1624. var CHARACTER_TRANSLATION = {
  1625. 0x2a: 0xe1, // á
  1626. 0x5c: 0xe9, // é
  1627. 0x5e: 0xed, // í
  1628. 0x5f: 0xf3, // ó
  1629. 0x60: 0xfa, // ú
  1630. 0x7b: 0xe7, // ç
  1631. 0x7c: 0xf7, // ÷
  1632. 0x7d: 0xd1, // Ñ
  1633. 0x7e: 0xf1, // ñ
  1634. 0x7f: 0x2588, // █
  1635. 0x0130: 0xae, // ®
  1636. 0x0131: 0xb0, // °
  1637. 0x0132: 0xbd, // ½
  1638. 0x0133: 0xbf, // ¿
  1639. 0x0134: 0x2122, // ™
  1640. 0x0135: 0xa2, // ¢
  1641. 0x0136: 0xa3, // £
  1642. 0x0137: 0x266a, // ♪
  1643. 0x0138: 0xe0, // à
  1644. 0x0139: 0xa0, //
  1645. 0x013a: 0xe8, // è
  1646. 0x013b: 0xe2, // â
  1647. 0x013c: 0xea, // ê
  1648. 0x013d: 0xee, // î
  1649. 0x013e: 0xf4, // ô
  1650. 0x013f: 0xfb, // û
  1651. 0x0220: 0xc1, // Á
  1652. 0x0221: 0xc9, // É
  1653. 0x0222: 0xd3, // Ó
  1654. 0x0223: 0xda, // Ú
  1655. 0x0224: 0xdc, // Ü
  1656. 0x0225: 0xfc, // ü
  1657. 0x0226: 0x2018, // ‘
  1658. 0x0227: 0xa1, // ¡
  1659. 0x0228: 0x2a, // *
  1660. 0x0229: 0x27, // '
  1661. 0x022a: 0x2014, // —
  1662. 0x022b: 0xa9, // ©
  1663. 0x022c: 0x2120, // ℠
  1664. 0x022d: 0x2022, // •
  1665. 0x022e: 0x201c, // “
  1666. 0x022f: 0x201d, // ”
  1667. 0x0230: 0xc0, // À
  1668. 0x0231: 0xc2, // Â
  1669. 0x0232: 0xc7, // Ç
  1670. 0x0233: 0xc8, // È
  1671. 0x0234: 0xca, // Ê
  1672. 0x0235: 0xcb, // Ë
  1673. 0x0236: 0xeb, // ë
  1674. 0x0237: 0xce, // Î
  1675. 0x0238: 0xcf, // Ï
  1676. 0x0239: 0xef, // ï
  1677. 0x023a: 0xd4, // Ô
  1678. 0x023b: 0xd9, // Ù
  1679. 0x023c: 0xf9, // ù
  1680. 0x023d: 0xdb, // Û
  1681. 0x023e: 0xab, // «
  1682. 0x023f: 0xbb, // »
  1683. 0x0320: 0xc3, // Ã
  1684. 0x0321: 0xe3, // ã
  1685. 0x0322: 0xcd, // Í
  1686. 0x0323: 0xcc, // Ì
  1687. 0x0324: 0xec, // ì
  1688. 0x0325: 0xd2, // Ò
  1689. 0x0326: 0xf2, // ò
  1690. 0x0327: 0xd5, // Õ
  1691. 0x0328: 0xf5, // õ
  1692. 0x0329: 0x7b, // {
  1693. 0x032a: 0x7d, // }
  1694. 0x032b: 0x5c, // \
  1695. 0x032c: 0x5e, // ^
  1696. 0x032d: 0x5f, // _
  1697. 0x032e: 0x7c, // |
  1698. 0x032f: 0x7e, // ~
  1699. 0x0330: 0xc4, // Ä
  1700. 0x0331: 0xe4, // ä
  1701. 0x0332: 0xd6, // Ö
  1702. 0x0333: 0xf6, // ö
  1703. 0x0334: 0xdf, // ß
  1704. 0x0335: 0xa5, // ¥
  1705. 0x0336: 0xa4, // ¤
  1706. 0x0337: 0x2502, // │
  1707. 0x0338: 0xc5, // Å
  1708. 0x0339: 0xe5, // å
  1709. 0x033a: 0xd8, // Ø
  1710. 0x033b: 0xf8, // ø
  1711. 0x033c: 0x250c, // ┌
  1712. 0x033d: 0x2510, // ┐
  1713. 0x033e: 0x2514, // └
  1714. 0x033f: 0x2518 // ┘
  1715. };
  1716. var getCharFromCode = function(code) {
  1717. if (code === null) {
  1718. return '';
  1719. }
  1720. code = CHARACTER_TRANSLATION[code] || code;
  1721. return String.fromCharCode(code);
  1722. };
  1723. // the index of the last row in a CEA-608 display buffer
  1724. var BOTTOM_ROW = 14;
  1725. // This array is used for mapping PACs -> row #, since there's no way of
  1726. // getting it through bit logic.
  1727. var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
  1728. 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
  1729. // CEA-608 captions are rendered onto a 34x15 matrix of character
  1730. // cells. The "bottom" row is the last element in the outer array.
  1731. var createDisplayBuffer = function() {
  1732. var result = [], i = BOTTOM_ROW + 1;
  1733. while (i--) {
  1734. result.push('');
  1735. }
  1736. return result;
  1737. };
  1738. var Cea608Stream = function(field, dataChannel) {
  1739. Cea608Stream.prototype.init.call(this);
  1740. this.field_ = field || 0;
  1741. this.dataChannel_ = dataChannel || 0;
  1742. this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
  1743. this.setConstants();
  1744. this.reset();
  1745. this.push = function(packet) {
  1746. var data, swap, char0, char1, text;
  1747. // remove the parity bits
  1748. data = packet.ccData & 0x7f7f;
  1749. // ignore duplicate control codes; the spec demands they're sent twice
  1750. if (data === this.lastControlCode_) {
  1751. this.lastControlCode_ = null;
  1752. return;
  1753. }
  1754. // Store control codes
  1755. if ((data & 0xf000) === 0x1000) {
  1756. this.lastControlCode_ = data;
  1757. } else if (data !== this.PADDING_) {
  1758. this.lastControlCode_ = null;
  1759. }
  1760. char0 = data >>> 8;
  1761. char1 = data & 0xff;
  1762. if (data === this.PADDING_) {
  1763. return;
  1764. } else if (data === this.RESUME_CAPTION_LOADING_) {
  1765. this.mode_ = 'popOn';
  1766. } else if (data === this.END_OF_CAPTION_) {
  1767. this.clearFormatting(packet.pts);
  1768. // if a caption was being displayed, it's gone now
  1769. this.flushDisplayed(packet.pts);
  1770. // flip memory
  1771. swap = this.displayed_;
  1772. this.displayed_ = this.nonDisplayed_;
  1773. this.nonDisplayed_ = swap;
  1774. // start measuring the time to display the caption
  1775. this.startPts_ = packet.pts;
  1776. } else if (data === this.ROLL_UP_2_ROWS_) {
  1777. this.topRow_ = BOTTOM_ROW - 1;
  1778. this.mode_ = 'rollUp';
  1779. } else if (data === this.ROLL_UP_3_ROWS_) {
  1780. this.topRow_ = BOTTOM_ROW - 2;
  1781. this.mode_ = 'rollUp';
  1782. } else if (data === this.ROLL_UP_4_ROWS_) {
  1783. this.topRow_ = BOTTOM_ROW - 3;
  1784. this.mode_ = 'rollUp';
  1785. } else if (data === this.CARRIAGE_RETURN_) {
  1786. this.clearFormatting(packet.pts);
  1787. this.flushDisplayed(packet.pts);
  1788. this.shiftRowsUp_();
  1789. this.startPts_ = packet.pts;
  1790. } else if (data === this.BACKSPACE_) {
  1791. if (this.mode_ === 'popOn') {
  1792. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  1793. } else {
  1794. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  1795. }
  1796. } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
  1797. this.flushDisplayed(packet.pts);
  1798. this.displayed_ = createDisplayBuffer();
  1799. } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
  1800. this.nonDisplayed_ = createDisplayBuffer();
  1801. } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
  1802. this.mode_ = 'paintOn';
  1803. // Append special characters to caption text
  1804. } else if (this.isSpecialCharacter(char0, char1)) {
  1805. // Bitmask char0 so that we can apply character transformations
  1806. // regardless of field and data channel.
  1807. // Then byte-shift to the left and OR with char1 so we can pass the
  1808. // entire character code to `getCharFromCode`.
  1809. char0 = (char0 & 0x03) << 8;
  1810. text = getCharFromCode(char0 | char1);
  1811. this[this.mode_](packet.pts, text);
  1812. this.column_++;
  1813. // Append extended characters to caption text
  1814. } else if (this.isExtCharacter(char0, char1)) {
  1815. // Extended characters always follow their "non-extended" equivalents.
  1816. // IE if a "è" is desired, you'll always receive "eè"; non-compliant
  1817. // decoders are supposed to drop the "è", while compliant decoders
  1818. // backspace the "e" and insert "è".
  1819. // Delete the previous character
  1820. if (this.mode_ === 'popOn') {
  1821. this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
  1822. } else {
  1823. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  1824. }
  1825. // Bitmask char0 so that we can apply character transformations
  1826. // regardless of field and data channel.
  1827. // Then byte-shift to the left and OR with char1 so we can pass the
  1828. // entire character code to `getCharFromCode`.
  1829. char0 = (char0 & 0x03) << 8;
  1830. text = getCharFromCode(char0 | char1);
  1831. this[this.mode_](packet.pts, text);
  1832. this.column_++;
  1833. // Process mid-row codes
  1834. } else if (this.isMidRowCode(char0, char1)) {
  1835. // Attributes are not additive, so clear all formatting
  1836. this.clearFormatting(packet.pts);
  1837. // According to the standard, mid-row codes
  1838. // should be replaced with spaces, so add one now
  1839. this[this.mode_](packet.pts, ' ');
  1840. this.column_++;
  1841. if ((char1 & 0xe) === 0xe) {
  1842. this.addFormatting(packet.pts, ['i']);
  1843. }
  1844. if ((char1 & 0x1) === 0x1) {
  1845. this.addFormatting(packet.pts, ['u']);
  1846. }
  1847. // Detect offset control codes and adjust cursor
  1848. } else if (this.isOffsetControlCode(char0, char1)) {
  1849. // Cursor position is set by indent PAC (see below) in 4-column
  1850. // increments, with an additional offset code of 1-3 to reach any
  1851. // of the 32 columns specified by CEA-608. So all we need to do
  1852. // here is increment the column cursor by the given offset.
  1853. this.column_ += (char1 & 0x03);
  1854. // Detect PACs (Preamble Address Codes)
  1855. } else if (this.isPAC(char0, char1)) {
  1856. // There's no logic for PAC -> row mapping, so we have to just
  1857. // find the row code in an array and use its index :(
  1858. var row = ROWS.indexOf(data & 0x1f20);
  1859. if (row !== this.row_) {
  1860. // formatting is only persistent for current row
  1861. this.clearFormatting(packet.pts);
  1862. this.row_ = row;
  1863. }
  1864. // All PACs can apply underline, so detect and apply
  1865. // (All odd-numbered second bytes set underline)
  1866. if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
  1867. this.addFormatting(packet.pts, ['u']);
  1868. }
  1869. if ((data & 0x10) === 0x10) {
  1870. // We've got an indent level code. Each successive even number
  1871. // increments the column cursor by 4, so we can get the desired
  1872. // column position by bit-shifting to the right (to get n/2)
  1873. // and multiplying by 4.
  1874. this.column_ = ((data & 0xe) >> 1) * 4;
  1875. }
  1876. if (this.isColorPAC(char1)) {
  1877. // it's a color code, though we only support white, which
  1878. // can be either normal or italicized. white italics can be
  1879. // either 0x4e or 0x6e depending on the row, so we just
  1880. // bitwise-and with 0xe to see if italics should be turned on
  1881. if ((char1 & 0xe) === 0xe) {
  1882. this.addFormatting(packet.pts, ['i']);
  1883. }
  1884. }
  1885. // We have a normal character in char0, and possibly one in char1
  1886. } else if (this.isNormalChar(char0)) {
  1887. if (char1 === 0x00) {
  1888. char1 = null;
  1889. }
  1890. text = getCharFromCode(char0);
  1891. text += getCharFromCode(char1);
  1892. this[this.mode_](packet.pts, text);
  1893. this.column_ += text.length;
  1894. } // finish data processing
  1895. };
  1896. };
  1897. Cea608Stream.prototype = new Stream();
  1898. // Trigger a cue point that captures the current state of the
  1899. // display buffer
  1900. Cea608Stream.prototype.flushDisplayed = function(pts) {
  1901. var content = this.displayed_
  1902. // remove spaces from the start and end of the string
  1903. .map(function(row) {
  1904. return row.trim();
  1905. })
  1906. // combine all text rows to display in one cue
  1907. .join('\n')
  1908. // and remove blank rows from the start and end, but not the middle
  1909. .replace(/^\n+|\n+$/g, '');
  1910. if (content.length) {
  1911. this.trigger('data', {
  1912. startPts: this.startPts_,
  1913. endPts: pts,
  1914. text: content,
  1915. stream: this.name_
  1916. });
  1917. }
  1918. };
  1919. /**
  1920. * Zero out the data, used for startup and on seek
  1921. */
  1922. Cea608Stream.prototype.reset = function() {
  1923. this.mode_ = 'popOn';
  1924. // When in roll-up mode, the index of the last row that will
  1925. // actually display captions. If a caption is shifted to a row
  1926. // with a lower index than this, it is cleared from the display
  1927. // buffer
  1928. this.topRow_ = 0;
  1929. this.startPts_ = 0;
  1930. this.displayed_ = createDisplayBuffer();
  1931. this.nonDisplayed_ = createDisplayBuffer();
  1932. this.lastControlCode_ = null;
  1933. // Track row and column for proper line-breaking and spacing
  1934. this.column_ = 0;
  1935. this.row_ = BOTTOM_ROW;
  1936. // This variable holds currently-applied formatting
  1937. this.formatting_ = [];
  1938. };
  1939. /**
  1940. * Sets up control code and related constants for this instance
  1941. */
  1942. Cea608Stream.prototype.setConstants = function() {
  1943. // The following attributes have these uses:
  1944. // ext_ : char0 for mid-row codes, and the base for extended
  1945. // chars (ext_+0, ext_+1, and ext_+2 are char0s for
  1946. // extended codes)
  1947. // control_: char0 for control codes, except byte-shifted to the
  1948. // left so that we can do this.control_ | CONTROL_CODE
  1949. // offset_: char0 for tab offset codes
  1950. //
  1951. // It's also worth noting that control codes, and _only_ control codes,
  1952. // differ between field 1 and field2. Field 2 control codes are always
  1953. // their field 1 value plus 1. That's why there's the "| field" on the
  1954. // control value.
  1955. if (this.dataChannel_ === 0) {
  1956. this.BASE_ = 0x10;
  1957. this.EXT_ = 0x11;
  1958. this.CONTROL_ = (0x14 | this.field_) << 8;
  1959. this.OFFSET_ = 0x17;
  1960. } else if (this.dataChannel_ === 1) {
  1961. this.BASE_ = 0x18;
  1962. this.EXT_ = 0x19;
  1963. this.CONTROL_ = (0x1c | this.field_) << 8;
  1964. this.OFFSET_ = 0x1f;
  1965. }
  1966. // Constants for the LSByte command codes recognized by Cea608Stream. This
  1967. // list is not exhaustive. For a more comprehensive listing and semantics see
  1968. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  1969. // Padding
  1970. this.PADDING_ = 0x0000;
  1971. // Pop-on Mode
  1972. this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
  1973. this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
  1974. // Roll-up Mode
  1975. this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
  1976. this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
  1977. this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
  1978. this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
  1979. // paint-on mode (not supported)
  1980. this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
  1981. // Erasure
  1982. this.BACKSPACE_ = this.CONTROL_ | 0x21;
  1983. this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
  1984. this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
  1985. };
  1986. /**
  1987. * Detects if the 2-byte packet data is a special character
  1988. *
  1989. * Special characters have a second byte in the range 0x30 to 0x3f,
  1990. * with the first byte being 0x11 (for data channel 1) or 0x19 (for
  1991. * data channel 2).
  1992. *
  1993. * @param {Integer} char0 The first byte
  1994. * @param {Integer} char1 The second byte
  1995. * @return {Boolean} Whether the 2 bytes are an special character
  1996. */
  1997. Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
  1998. return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
  1999. };
  2000. /**
  2001. * Detects if the 2-byte packet data is an extended character
  2002. *
  2003. * Extended characters have a second byte in the range 0x20 to 0x3f,
  2004. * with the first byte being 0x12 or 0x13 (for data channel 1) or
  2005. * 0x1a or 0x1b (for data channel 2).
  2006. *
  2007. * @param {Integer} char0 The first byte
  2008. * @param {Integer} char1 The second byte
  2009. * @return {Boolean} Whether the 2 bytes are an extended character
  2010. */
  2011. Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
  2012. return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
  2013. (char1 >= 0x20 && char1 <= 0x3f));
  2014. };
  2015. /**
  2016. * Detects if the 2-byte packet is a mid-row code
  2017. *
  2018. * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
  2019. * the first byte being 0x11 (for data channel 1) or 0x19 (for data
  2020. * channel 2).
  2021. *
  2022. * @param {Integer} char0 The first byte
  2023. * @param {Integer} char1 The second byte
  2024. * @return {Boolean} Whether the 2 bytes are a mid-row code
  2025. */
  2026. Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
  2027. return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
  2028. };
  2029. /**
  2030. * Detects if the 2-byte packet is an offset control code
  2031. *
  2032. * Offset control codes have a second byte in the range 0x21 to 0x23,
  2033. * with the first byte being 0x17 (for data channel 1) or 0x1f (for
  2034. * data channel 2).
  2035. *
  2036. * @param {Integer} char0 The first byte
  2037. * @param {Integer} char1 The second byte
  2038. * @return {Boolean} Whether the 2 bytes are an offset control code
  2039. */
  2040. Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
  2041. return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
  2042. };
  2043. /**
  2044. * Detects if the 2-byte packet is a Preamble Address Code
  2045. *
  2046. * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
  2047. * or 0x18 to 0x1f (for data channel 2), with the second byte in the
  2048. * range 0x40 to 0x7f.
  2049. *
  2050. * @param {Integer} char0 The first byte
  2051. * @param {Integer} char1 The second byte
  2052. * @return {Boolean} Whether the 2 bytes are a PAC
  2053. */
  2054. Cea608Stream.prototype.isPAC = function(char0, char1) {
  2055. return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
  2056. (char1 >= 0x40 && char1 <= 0x7f));
  2057. };
  2058. /**
  2059. * Detects if a packet's second byte is in the range of a PAC color code
  2060. *
  2061. * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
  2062. * 0x60 to 0x6f.
  2063. *
  2064. * @param {Integer} char1 The second byte
  2065. * @return {Boolean} Whether the byte is a color PAC
  2066. */
  2067. Cea608Stream.prototype.isColorPAC = function(char1) {
  2068. return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
  2069. };
  2070. /**
  2071. * Detects if a single byte is in the range of a normal character
  2072. *
  2073. * Normal text bytes are in the range 0x20 to 0x7f.
  2074. *
  2075. * @param {Integer} char The byte
  2076. * @return {Boolean} Whether the byte is a normal character
  2077. */
  2078. Cea608Stream.prototype.isNormalChar = function(char) {
  2079. return (char >= 0x20 && char <= 0x7f);
  2080. };
  2081. // Adds the opening HTML tag for the passed character to the caption text,
  2082. // and keeps track of it for later closing
  2083. Cea608Stream.prototype.addFormatting = function(pts, format) {
  2084. this.formatting_ = this.formatting_.concat(format);
  2085. var text = format.reduce(function(text, format) {
  2086. return text + '<' + format + '>';
  2087. }, '');
  2088. this[this.mode_](pts, text);
  2089. };
  2090. // Adds HTML closing tags for current formatting to caption text and
  2091. // clears remembered formatting
  2092. Cea608Stream.prototype.clearFormatting = function(pts) {
  2093. if (!this.formatting_.length) {
  2094. return;
  2095. }
  2096. var text = this.formatting_.reverse().reduce(function(text, format) {
  2097. return text + '</' + format + '>';
  2098. }, '');
  2099. this.formatting_ = [];
  2100. this[this.mode_](pts, text);
  2101. };
  2102. // Mode Implementations
  2103. Cea608Stream.prototype.popOn = function(pts, text) {
  2104. var baseRow = this.nonDisplayed_[this.row_];
  2105. // buffer characters
  2106. baseRow += text;
  2107. this.nonDisplayed_[this.row_] = baseRow;
  2108. };
  2109. Cea608Stream.prototype.rollUp = function(pts, text) {
  2110. var baseRow = this.displayed_[BOTTOM_ROW];
  2111. baseRow += text;
  2112. this.displayed_[BOTTOM_ROW] = baseRow;
  2113. };
  2114. Cea608Stream.prototype.shiftRowsUp_ = function() {
  2115. var i;
  2116. // clear out inactive rows
  2117. for (i = 0; i < this.topRow_; i++) {
  2118. this.displayed_[i] = '';
  2119. }
  2120. // shift displayed rows up
  2121. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  2122. this.displayed_[i] = this.displayed_[i + 1];
  2123. }
  2124. // clear out the bottom row
  2125. this.displayed_[BOTTOM_ROW] = '';
  2126. };
  2127. // paintOn mode is not implemented
  2128. Cea608Stream.prototype.paintOn = function() {};
  2129. // exports
  2130. module.exports = {
  2131. CaptionStream: CaptionStream,
  2132. Cea608Stream: Cea608Stream
  2133. };
  2134. },{"../utils/stream":15}],10:[function(require,module,exports){
  2135. /**
  2136. * mux.js
  2137. *
  2138. * Copyright (c) 2015 Brightcove
  2139. * All rights reserved.
  2140. *
  2141. * A stream-based mp2t to mp4 converter. This utility can be used to
  2142. * deliver mp4s to a SourceBuffer on platforms that support native
  2143. * Media Source Extensions.
  2144. */
  2145. 'use strict';
  2146. var Stream = require('../utils/stream.js'),
  2147. CaptionStream = require('./caption-stream'),
  2148. StreamTypes = require('./stream-types'),
  2149. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  2150. var m2tsStreamTypes = require('./stream-types.js');
  2151. // object types
  2152. var TransportPacketStream, TransportParseStream, ElementaryStream;
  2153. // constants
  2154. var
  2155. MP2T_PACKET_LENGTH = 188, // bytes
  2156. SYNC_BYTE = 0x47;
  2157. /**
  2158. * Splits an incoming stream of binary data into MPEG-2 Transport
  2159. * Stream packets.
  2160. */
  2161. TransportPacketStream = function() {
  2162. var
  2163. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  2164. bytesInBuffer = 0;
  2165. TransportPacketStream.prototype.init.call(this);
  2166. // Deliver new bytes to the stream.
  2167. this.push = function(bytes) {
  2168. var
  2169. startIndex = 0,
  2170. endIndex = MP2T_PACKET_LENGTH,
  2171. everything;
  2172. // If there are bytes remaining from the last segment, prepend them to the
  2173. // bytes that were pushed in
  2174. if (bytesInBuffer) {
  2175. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  2176. everything.set(buffer.subarray(0, bytesInBuffer));
  2177. everything.set(bytes, bytesInBuffer);
  2178. bytesInBuffer = 0;
  2179. } else {
  2180. everything = bytes;
  2181. }
  2182. // While we have enough data for a packet
  2183. while (endIndex < everything.byteLength) {
  2184. // Look for a pair of start and end sync bytes in the data..
  2185. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  2186. // We found a packet so emit it and jump one whole packet forward in
  2187. // the stream
  2188. this.trigger('data', everything.subarray(startIndex, endIndex));
  2189. startIndex += MP2T_PACKET_LENGTH;
  2190. endIndex += MP2T_PACKET_LENGTH;
  2191. continue;
  2192. }
  2193. // If we get here, we have somehow become de-synchronized and we need to step
  2194. // forward one byte at a time until we find a pair of sync bytes that denote
  2195. // a packet
  2196. startIndex++;
  2197. endIndex++;
  2198. }
  2199. // If there was some data left over at the end of the segment that couldn't
  2200. // possibly be a whole packet, keep it because it might be the start of a packet
  2201. // that continues in the next segment
  2202. if (startIndex < everything.byteLength) {
  2203. buffer.set(everything.subarray(startIndex), 0);
  2204. bytesInBuffer = everything.byteLength - startIndex;
  2205. }
  2206. };
  2207. this.flush = function() {
  2208. // If the buffer contains a whole packet when we are being flushed, emit it
  2209. // and empty the buffer. Otherwise hold onto the data because it may be
  2210. // important for decoding the next segment
  2211. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  2212. this.trigger('data', buffer);
  2213. bytesInBuffer = 0;
  2214. }
  2215. this.trigger('done');
  2216. };
  2217. };
  2218. TransportPacketStream.prototype = new Stream();
  2219. /**
  2220. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  2221. * forms of the individual transport stream packets.
  2222. */
  2223. TransportParseStream = function() {
  2224. var parsePsi, parsePat, parsePmt, self;
  2225. TransportParseStream.prototype.init.call(this);
  2226. self = this;
  2227. this.packetsWaitingForPmt = [];
  2228. this.programMapTable = undefined;
  2229. parsePsi = function(payload, psi) {
  2230. var offset = 0;
  2231. // PSI packets may be split into multiple sections and those
  2232. // sections may be split into multiple packets. If a PSI
  2233. // section starts in this packet, the payload_unit_start_indicator
  2234. // will be true and the first byte of the payload will indicate
  2235. // the offset from the current position to the start of the
  2236. // section.
  2237. if (psi.payloadUnitStartIndicator) {
  2238. offset += payload[offset] + 1;
  2239. }
  2240. if (psi.type === 'pat') {
  2241. parsePat(payload.subarray(offset), psi);
  2242. } else {
  2243. parsePmt(payload.subarray(offset), psi);
  2244. }
  2245. };
  2246. parsePat = function(payload, pat) {
  2247. pat.section_number = payload[7]; // eslint-disable-line camelcase
  2248. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  2249. // skip the PSI header and parse the first PMT entry
  2250. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  2251. pat.pmtPid = self.pmtPid;
  2252. };
  2253. /**
  2254. * Parse out the relevant fields of a Program Map Table (PMT).
  2255. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  2256. * packet. The first byte in this array should be the table_id
  2257. * field.
  2258. * @param pmt {object} the object that should be decorated with
  2259. * fields parsed from the PMT.
  2260. */
  2261. parsePmt = function(payload, pmt) {
  2262. var sectionLength, tableEnd, programInfoLength, offset;
  2263. // PMTs can be sent ahead of the time when they should actually
  2264. // take effect. We don't believe this should ever be the case
  2265. // for HLS but we'll ignore "forward" PMT declarations if we see
  2266. // them. Future PMT declarations have the current_next_indicator
  2267. // set to zero.
  2268. if (!(payload[5] & 0x01)) {
  2269. return;
  2270. }
  2271. // overwrite any existing program map table
  2272. self.programMapTable = {
  2273. video: null,
  2274. audio: null,
  2275. 'timed-metadata': {}
  2276. };
  2277. // the mapping table ends at the end of the current section
  2278. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  2279. tableEnd = 3 + sectionLength - 4;
  2280. // to determine where the table is, we have to figure out how
  2281. // long the program info descriptors are
  2282. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  2283. // advance the offset to the first entry in the mapping table
  2284. offset = 12 + programInfoLength;
  2285. while (offset < tableEnd) {
  2286. var streamType = payload[offset];
  2287. var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
  2288. // only map a single elementary_pid for audio and video stream types
  2289. // TODO: should this be done for metadata too? for now maintain behavior of
  2290. // multiple metadata streams
  2291. if (streamType === StreamTypes.H264_STREAM_TYPE &&
  2292. self.programMapTable.video === null) {
  2293. self.programMapTable.video = pid;
  2294. } else if (streamType === StreamTypes.ADTS_STREAM_TYPE &&
  2295. self.programMapTable.audio === null) {
  2296. self.programMapTable.audio = pid;
  2297. } else if (streamType === StreamTypes.METADATA_STREAM_TYPE) {
  2298. // map pid to stream type for metadata streams
  2299. self.programMapTable['timed-metadata'][pid] = streamType;
  2300. }
  2301. // move to the next table entry
  2302. // skip past the elementary stream descriptors, if present
  2303. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  2304. }
  2305. // record the map on the packet as well
  2306. pmt.programMapTable = self.programMapTable;
  2307. };
  2308. /**
  2309. * Deliver a new MP2T packet to the stream.
  2310. */
  2311. this.push = function(packet) {
  2312. var
  2313. result = {},
  2314. offset = 4;
  2315. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  2316. // pid is a 13-bit field starting at the last bit of packet[1]
  2317. result.pid = packet[1] & 0x1f;
  2318. result.pid <<= 8;
  2319. result.pid |= packet[2];
  2320. // if an adaption field is present, its length is specified by the
  2321. // fifth byte of the TS packet header. The adaptation field is
  2322. // used to add stuffing to PES packets that don't fill a complete
  2323. // TS packet, and to specify some forms of timing and control data
  2324. // that we do not currently use.
  2325. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  2326. offset += packet[offset] + 1;
  2327. }
  2328. // parse the rest of the packet based on the type
  2329. if (result.pid === 0) {
  2330. result.type = 'pat';
  2331. parsePsi(packet.subarray(offset), result);
  2332. this.trigger('data', result);
  2333. } else if (result.pid === this.pmtPid) {
  2334. result.type = 'pmt';
  2335. parsePsi(packet.subarray(offset), result);
  2336. this.trigger('data', result);
  2337. // if there are any packets waiting for a PMT to be found, process them now
  2338. while (this.packetsWaitingForPmt.length) {
  2339. this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
  2340. }
  2341. } else if (this.programMapTable === undefined) {
  2342. // When we have not seen a PMT yet, defer further processing of
  2343. // PES packets until one has been parsed
  2344. this.packetsWaitingForPmt.push([packet, offset, result]);
  2345. } else {
  2346. this.processPes_(packet, offset, result);
  2347. }
  2348. };
  2349. this.processPes_ = function(packet, offset, result) {
  2350. // set the appropriate stream type
  2351. if (result.pid === this.programMapTable.video) {
  2352. result.streamType = StreamTypes.H264_STREAM_TYPE;
  2353. } else if (result.pid === this.programMapTable.audio) {
  2354. result.streamType = StreamTypes.ADTS_STREAM_TYPE;
  2355. } else {
  2356. // if not video or audio, it is timed-metadata or unknown
  2357. // if unknown, streamType will be undefined
  2358. result.streamType = this.programMapTable['timed-metadata'][result.pid];
  2359. }
  2360. result.type = 'pes';
  2361. result.data = packet.subarray(offset);
  2362. this.trigger('data', result);
  2363. };
  2364. };
  2365. TransportParseStream.prototype = new Stream();
  2366. TransportParseStream.STREAM_TYPES = {
  2367. h264: 0x1b,
  2368. adts: 0x0f
  2369. };
  2370. /**
  2371. * Reconsistutes program elementary stream (PES) packets from parsed
  2372. * transport stream packets. That is, if you pipe an
  2373. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  2374. * events will be events which capture the bytes for individual PES
  2375. * packets plus relevant metadata that has been extracted from the
  2376. * container.
  2377. */
  2378. ElementaryStream = function() {
  2379. var
  2380. self = this,
  2381. // PES packet fragments
  2382. video = {
  2383. data: [],
  2384. size: 0
  2385. },
  2386. audio = {
  2387. data: [],
  2388. size: 0
  2389. },
  2390. timedMetadata = {
  2391. data: [],
  2392. size: 0
  2393. },
  2394. parsePes = function(payload, pes) {
  2395. var ptsDtsFlags;
  2396. // get the packet length, this will be 0 for video
  2397. pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
  2398. // find out if this packets starts a new keyframe
  2399. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  2400. // PES packets may be annotated with a PTS value, or a PTS value
  2401. // and a DTS value. Determine what combination of values is
  2402. // available to work with.
  2403. ptsDtsFlags = payload[7];
  2404. // PTS and DTS are normally stored as a 33-bit number. Javascript
  2405. // performs all bitwise operations on 32-bit integers but javascript
  2406. // supports a much greater range (52-bits) of integer using standard
  2407. // mathematical operations.
  2408. // We construct a 31-bit value using bitwise operators over the 31
  2409. // most significant bits and then multiply by 4 (equal to a left-shift
  2410. // of 2) before we add the final 2 least significant bits of the
  2411. // timestamp (equal to an OR.)
  2412. if (ptsDtsFlags & 0xC0) {
  2413. // the PTS and DTS are not written out directly. For information
  2414. // on how they are encoded, see
  2415. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  2416. pes.pts = (payload[9] & 0x0E) << 27 |
  2417. (payload[10] & 0xFF) << 20 |
  2418. (payload[11] & 0xFE) << 12 |
  2419. (payload[12] & 0xFF) << 5 |
  2420. (payload[13] & 0xFE) >>> 3;
  2421. pes.pts *= 4; // Left shift by 2
  2422. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  2423. pes.dts = pes.pts;
  2424. if (ptsDtsFlags & 0x40) {
  2425. pes.dts = (payload[14] & 0x0E) << 27 |
  2426. (payload[15] & 0xFF) << 20 |
  2427. (payload[16] & 0xFE) << 12 |
  2428. (payload[17] & 0xFF) << 5 |
  2429. (payload[18] & 0xFE) >>> 3;
  2430. pes.dts *= 4; // Left shift by 2
  2431. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  2432. }
  2433. }
  2434. // the data section starts immediately after the PES header.
  2435. // pes_header_data_length specifies the number of header bytes
  2436. // that follow the last byte of the field.
  2437. pes.data = payload.subarray(9 + payload[8]);
  2438. },
  2439. flushStream = function(stream, type, forceFlush) {
  2440. var
  2441. packetData = new Uint8Array(stream.size),
  2442. event = {
  2443. type: type
  2444. },
  2445. i = 0,
  2446. offset = 0,
  2447. packetFlushable = false,
  2448. fragment;
  2449. // do nothing if there is not enough buffered data for a complete
  2450. // PES header
  2451. if (!stream.data.length || stream.size < 9) {
  2452. return;
  2453. }
  2454. event.trackId = stream.data[0].pid;
  2455. // reassemble the packet
  2456. for (i = 0; i < stream.data.length; i++) {
  2457. fragment = stream.data[i];
  2458. packetData.set(fragment.data, offset);
  2459. offset += fragment.data.byteLength;
  2460. }
  2461. // parse assembled packet's PES header
  2462. parsePes(packetData, event);
  2463. // non-video PES packets MUST have a non-zero PES_packet_length
  2464. // check that there is enough stream data to fill the packet
  2465. packetFlushable = type === 'video' || event.packetLength <= stream.size;
  2466. // flush pending packets if the conditions are right
  2467. if (forceFlush || packetFlushable) {
  2468. stream.size = 0;
  2469. stream.data.length = 0;
  2470. }
  2471. // only emit packets that are complete. this is to avoid assembling
  2472. // incomplete PES packets due to poor segmentation
  2473. if (packetFlushable) {
  2474. self.trigger('data', event);
  2475. }
  2476. };
  2477. ElementaryStream.prototype.init.call(this);
  2478. this.push = function(data) {
  2479. ({
  2480. pat: function() {
  2481. // we have to wait for the PMT to arrive as well before we
  2482. // have any meaningful metadata
  2483. },
  2484. pes: function() {
  2485. var stream, streamType;
  2486. switch (data.streamType) {
  2487. case StreamTypes.H264_STREAM_TYPE:
  2488. case m2tsStreamTypes.H264_STREAM_TYPE:
  2489. stream = video;
  2490. streamType = 'video';
  2491. break;
  2492. case StreamTypes.ADTS_STREAM_TYPE:
  2493. stream = audio;
  2494. streamType = 'audio';
  2495. break;
  2496. case StreamTypes.METADATA_STREAM_TYPE:
  2497. stream = timedMetadata;
  2498. streamType = 'timed-metadata';
  2499. break;
  2500. default:
  2501. // ignore unknown stream types
  2502. return;
  2503. }
  2504. // if a new packet is starting, we can flush the completed
  2505. // packet
  2506. if (data.payloadUnitStartIndicator) {
  2507. flushStream(stream, streamType, true);
  2508. }
  2509. // buffer this fragment until we are sure we've received the
  2510. // complete payload
  2511. stream.data.push(data);
  2512. stream.size += data.data.byteLength;
  2513. },
  2514. pmt: function() {
  2515. var
  2516. event = {
  2517. type: 'metadata',
  2518. tracks: []
  2519. },
  2520. programMapTable = data.programMapTable;
  2521. // translate audio and video streams to tracks
  2522. if (programMapTable.video !== null) {
  2523. event.tracks.push({
  2524. timelineStartInfo: {
  2525. baseMediaDecodeTime: 0
  2526. },
  2527. id: +programMapTable.video,
  2528. codec: 'avc',
  2529. type: 'video'
  2530. });
  2531. }
  2532. if (programMapTable.audio !== null) {
  2533. event.tracks.push({
  2534. timelineStartInfo: {
  2535. baseMediaDecodeTime: 0
  2536. },
  2537. id: +programMapTable.audio,
  2538. codec: 'adts',
  2539. type: 'audio'
  2540. });
  2541. }
  2542. self.trigger('data', event);
  2543. }
  2544. })[data.type]();
  2545. };
  2546. /**
  2547. * Flush any remaining input. Video PES packets may be of variable
  2548. * length. Normally, the start of a new video packet can trigger the
  2549. * finalization of the previous packet. That is not possible if no
  2550. * more video is forthcoming, however. In that case, some other
  2551. * mechanism (like the end of the file) has to be employed. When it is
  2552. * clear that no additional data is forthcoming, calling this method
  2553. * will flush the buffered packets.
  2554. */
  2555. this.flush = function() {
  2556. // !!THIS ORDER IS IMPORTANT!!
  2557. // video first then audio
  2558. flushStream(video, 'video');
  2559. flushStream(audio, 'audio');
  2560. flushStream(timedMetadata, 'timed-metadata');
  2561. this.trigger('done');
  2562. };
  2563. };
  2564. ElementaryStream.prototype = new Stream();
  2565. var m2ts = {
  2566. PAT_PID: 0x0000,
  2567. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  2568. TransportPacketStream: TransportPacketStream,
  2569. TransportParseStream: TransportParseStream,
  2570. ElementaryStream: ElementaryStream,
  2571. TimestampRolloverStream: TimestampRolloverStream,
  2572. CaptionStream: CaptionStream.CaptionStream,
  2573. Cea608Stream: CaptionStream.Cea608Stream,
  2574. MetadataStream: require('./metadata-stream')
  2575. };
  2576. for (var type in StreamTypes) {
  2577. if (StreamTypes.hasOwnProperty(type)) {
  2578. m2ts[type] = StreamTypes[type];
  2579. }
  2580. }
  2581. module.exports = m2ts;
  2582. },{"../utils/stream.js":15,"./caption-stream":9,"./metadata-stream":11,"./stream-types":12,"./stream-types.js":12,"./timestamp-rollover-stream":13}],11:[function(require,module,exports){
  2583. /**
  2584. * Accepts program elementary stream (PES) data events and parses out
  2585. * ID3 metadata from them, if present.
  2586. * @see http://id3.org/id3v2.3.0
  2587. */
  2588. 'use strict';
  2589. var
  2590. Stream = require('../utils/stream'),
  2591. StreamTypes = require('./stream-types'),
  2592. // return a percent-encoded representation of the specified byte range
  2593. // @see http://en.wikipedia.org/wiki/Percent-encoding
  2594. percentEncode = function(bytes, start, end) {
  2595. var i, result = '';
  2596. for (i = start; i < end; i++) {
  2597. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  2598. }
  2599. return result;
  2600. },
  2601. // return the string representation of the specified byte range,
  2602. // interpreted as UTf-8.
  2603. parseUtf8 = function(bytes, start, end) {
  2604. return decodeURIComponent(percentEncode(bytes, start, end));
  2605. },
  2606. // return the string representation of the specified byte range,
  2607. // interpreted as ISO-8859-1.
  2608. parseIso88591 = function(bytes, start, end) {
  2609. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  2610. },
  2611. parseSyncSafeInteger = function(data) {
  2612. return (data[0] << 21) |
  2613. (data[1] << 14) |
  2614. (data[2] << 7) |
  2615. (data[3]);
  2616. },
  2617. tagParsers = {
  2618. TXXX: function(tag) {
  2619. var i;
  2620. if (tag.data[0] !== 3) {
  2621. // ignore frames with unrecognized character encodings
  2622. return;
  2623. }
  2624. for (i = 1; i < tag.data.length; i++) {
  2625. if (tag.data[i] === 0) {
  2626. // parse the text fields
  2627. tag.description = parseUtf8(tag.data, 1, i);
  2628. // do not include the null terminator in the tag value
  2629. tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
  2630. break;
  2631. }
  2632. }
  2633. tag.data = tag.value;
  2634. },
  2635. WXXX: function(tag) {
  2636. var i;
  2637. if (tag.data[0] !== 3) {
  2638. // ignore frames with unrecognized character encodings
  2639. return;
  2640. }
  2641. for (i = 1; i < tag.data.length; i++) {
  2642. if (tag.data[i] === 0) {
  2643. // parse the description and URL fields
  2644. tag.description = parseUtf8(tag.data, 1, i);
  2645. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  2646. break;
  2647. }
  2648. }
  2649. },
  2650. PRIV: function(tag) {
  2651. var i;
  2652. for (i = 0; i < tag.data.length; i++) {
  2653. if (tag.data[i] === 0) {
  2654. // parse the description and URL fields
  2655. tag.owner = parseIso88591(tag.data, 0, i);
  2656. break;
  2657. }
  2658. }
  2659. tag.privateData = tag.data.subarray(i + 1);
  2660. tag.data = tag.privateData;
  2661. }
  2662. },
  2663. MetadataStream;
  2664. MetadataStream = function(options) {
  2665. var
  2666. settings = {
  2667. debug: !!(options && options.debug),
  2668. // the bytes of the program-level descriptor field in MP2T
  2669. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  2670. // program element descriptors"
  2671. descriptor: options && options.descriptor
  2672. },
  2673. // the total size in bytes of the ID3 tag being parsed
  2674. tagSize = 0,
  2675. // tag data that is not complete enough to be parsed
  2676. buffer = [],
  2677. // the total number of bytes currently in the buffer
  2678. bufferSize = 0,
  2679. i;
  2680. MetadataStream.prototype.init.call(this);
  2681. // calculate the text track in-band metadata track dispatch type
  2682. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  2683. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  2684. if (settings.descriptor) {
  2685. for (i = 0; i < settings.descriptor.length; i++) {
  2686. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  2687. }
  2688. }
  2689. this.push = function(chunk) {
  2690. var tag, frameStart, frameSize, frame, i, frameHeader;
  2691. if (chunk.type !== 'timed-metadata') {
  2692. return;
  2693. }
  2694. // if data_alignment_indicator is set in the PES header,
  2695. // we must have the start of a new ID3 tag. Assume anything
  2696. // remaining in the buffer was malformed and throw it out
  2697. if (chunk.dataAlignmentIndicator) {
  2698. bufferSize = 0;
  2699. buffer.length = 0;
  2700. }
  2701. // ignore events that don't look like ID3 data
  2702. if (buffer.length === 0 &&
  2703. (chunk.data.length < 10 ||
  2704. chunk.data[0] !== 'I'.charCodeAt(0) ||
  2705. chunk.data[1] !== 'D'.charCodeAt(0) ||
  2706. chunk.data[2] !== '3'.charCodeAt(0))) {
  2707. if (settings.debug) {
  2708. // eslint-disable-next-line no-console
  2709. console.log('Skipping unrecognized metadata packet');
  2710. }
  2711. return;
  2712. }
  2713. // add this chunk to the data we've collected so far
  2714. buffer.push(chunk);
  2715. bufferSize += chunk.data.byteLength;
  2716. // grab the size of the entire frame from the ID3 header
  2717. if (buffer.length === 1) {
  2718. // the frame size is transmitted as a 28-bit integer in the
  2719. // last four bytes of the ID3 header.
  2720. // The most significant bit of each byte is dropped and the
  2721. // results concatenated to recover the actual value.
  2722. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  2723. // ID3 reports the tag size excluding the header but it's more
  2724. // convenient for our comparisons to include it
  2725. tagSize += 10;
  2726. }
  2727. // if the entire frame has not arrived, wait for more data
  2728. if (bufferSize < tagSize) {
  2729. return;
  2730. }
  2731. // collect the entire frame so it can be parsed
  2732. tag = {
  2733. data: new Uint8Array(tagSize),
  2734. frames: [],
  2735. pts: buffer[0].pts,
  2736. dts: buffer[0].dts
  2737. };
  2738. for (i = 0; i < tagSize;) {
  2739. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  2740. i += buffer[0].data.byteLength;
  2741. bufferSize -= buffer[0].data.byteLength;
  2742. buffer.shift();
  2743. }
  2744. // find the start of the first frame and the end of the tag
  2745. frameStart = 10;
  2746. if (tag.data[5] & 0x40) {
  2747. // advance the frame start past the extended header
  2748. frameStart += 4; // header size field
  2749. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  2750. // clip any padding off the end
  2751. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  2752. }
  2753. // parse one or more ID3 frames
  2754. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  2755. do {
  2756. // determine the number of bytes in this frame
  2757. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  2758. if (frameSize < 1) {
  2759. // eslint-disable-next-line no-console
  2760. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  2761. }
  2762. frameHeader = String.fromCharCode(tag.data[frameStart],
  2763. tag.data[frameStart + 1],
  2764. tag.data[frameStart + 2],
  2765. tag.data[frameStart + 3]);
  2766. frame = {
  2767. id: frameHeader,
  2768. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  2769. };
  2770. frame.key = frame.id;
  2771. if (tagParsers[frame.id]) {
  2772. tagParsers[frame.id](frame);
  2773. // handle the special PRIV frame used to indicate the start
  2774. // time for raw AAC data
  2775. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  2776. var
  2777. d = frame.data,
  2778. size = ((d[3] & 0x01) << 30) |
  2779. (d[4] << 22) |
  2780. (d[5] << 14) |
  2781. (d[6] << 6) |
  2782. (d[7] >>> 2);
  2783. size *= 4;
  2784. size += d[7] & 0x03;
  2785. frame.timeStamp = size;
  2786. // in raw AAC, all subsequent data will be timestamped based
  2787. // on the value of this frame
  2788. // we couldn't have known the appropriate pts and dts before
  2789. // parsing this ID3 tag so set those values now
  2790. if (tag.pts === undefined && tag.dts === undefined) {
  2791. tag.pts = frame.timeStamp;
  2792. tag.dts = frame.timeStamp;
  2793. }
  2794. this.trigger('timestamp', frame);
  2795. }
  2796. }
  2797. tag.frames.push(frame);
  2798. frameStart += 10; // advance past the frame header
  2799. frameStart += frameSize; // advance past the frame body
  2800. } while (frameStart < tagSize);
  2801. this.trigger('data', tag);
  2802. };
  2803. };
  2804. MetadataStream.prototype = new Stream();
  2805. module.exports = MetadataStream;
  2806. },{"../utils/stream":15,"./stream-types":12}],12:[function(require,module,exports){
  2807. 'use strict';
  2808. module.exports = {
  2809. H264_STREAM_TYPE: 0x1B,
  2810. ADTS_STREAM_TYPE: 0x0F,
  2811. METADATA_STREAM_TYPE: 0x15
  2812. };
  2813. },{}],13:[function(require,module,exports){
  2814. /**
  2815. * mux.js
  2816. *
  2817. * Copyright (c) 2016 Brightcove
  2818. * All rights reserved.
  2819. *
  2820. * Accepts program elementary stream (PES) data events and corrects
  2821. * decode and presentation time stamps to account for a rollover
  2822. * of the 33 bit value.
  2823. */
  2824. 'use strict';
  2825. var Stream = require('../utils/stream');
  2826. var MAX_TS = 8589934592;
  2827. var RO_THRESH = 4294967296;
  2828. var handleRollover = function(value, reference) {
  2829. var direction = 1;
  2830. if (value > reference) {
  2831. // If the current timestamp value is greater than our reference timestamp and we detect a
  2832. // timestamp rollover, this means the roll over is happening in the opposite direction.
  2833. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  2834. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  2835. // rollover point. In loading this segment, the timestamp values will be very large,
  2836. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  2837. // the time stamp to be `value - 2^33`.
  2838. direction = -1;
  2839. }
  2840. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  2841. // cause an incorrect adjustment.
  2842. while (Math.abs(reference - value) > RO_THRESH) {
  2843. value += (direction * MAX_TS);
  2844. }
  2845. return value;
  2846. };
  2847. var TimestampRolloverStream = function(type) {
  2848. var lastDTS, referenceDTS;
  2849. TimestampRolloverStream.prototype.init.call(this);
  2850. this.type_ = type;
  2851. this.push = function(data) {
  2852. if (data.type !== this.type_) {
  2853. return;
  2854. }
  2855. if (referenceDTS === undefined) {
  2856. referenceDTS = data.dts;
  2857. }
  2858. data.dts = handleRollover(data.dts, referenceDTS);
  2859. data.pts = handleRollover(data.pts, referenceDTS);
  2860. lastDTS = data.dts;
  2861. this.trigger('data', data);
  2862. };
  2863. this.flush = function() {
  2864. referenceDTS = lastDTS;
  2865. this.trigger('done');
  2866. };
  2867. this.discontinuity = function() {
  2868. referenceDTS = void 0;
  2869. lastDTS = void 0;
  2870. };
  2871. };
  2872. TimestampRolloverStream.prototype = new Stream();
  2873. module.exports = {
  2874. TimestampRolloverStream: TimestampRolloverStream,
  2875. handleRollover: handleRollover
  2876. };
  2877. },{"../utils/stream":15}],14:[function(require,module,exports){
  2878. 'use strict';
  2879. var ExpGolomb;
  2880. /**
  2881. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  2882. * scheme used by h264.
  2883. */
  2884. ExpGolomb = function(workingData) {
  2885. var
  2886. // the number of bytes left to examine in workingData
  2887. workingBytesAvailable = workingData.byteLength,
  2888. // the current word being examined
  2889. workingWord = 0, // :uint
  2890. // the number of bits left to examine in the current word
  2891. workingBitsAvailable = 0; // :uint;
  2892. // ():uint
  2893. this.length = function() {
  2894. return (8 * workingBytesAvailable);
  2895. };
  2896. // ():uint
  2897. this.bitsAvailable = function() {
  2898. return (8 * workingBytesAvailable) + workingBitsAvailable;
  2899. };
  2900. // ():void
  2901. this.loadWord = function() {
  2902. var
  2903. position = workingData.byteLength - workingBytesAvailable,
  2904. workingBytes = new Uint8Array(4),
  2905. availableBytes = Math.min(4, workingBytesAvailable);
  2906. if (availableBytes === 0) {
  2907. throw new Error('no bytes available');
  2908. }
  2909. workingBytes.set(workingData.subarray(position,
  2910. position + availableBytes));
  2911. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  2912. // track the amount of workingData that has been processed
  2913. workingBitsAvailable = availableBytes * 8;
  2914. workingBytesAvailable -= availableBytes;
  2915. };
  2916. // (count:int):void
  2917. this.skipBits = function(count) {
  2918. var skipBytes; // :int
  2919. if (workingBitsAvailable > count) {
  2920. workingWord <<= count;
  2921. workingBitsAvailable -= count;
  2922. } else {
  2923. count -= workingBitsAvailable;
  2924. skipBytes = Math.floor(count / 8);
  2925. count -= (skipBytes * 8);
  2926. workingBytesAvailable -= skipBytes;
  2927. this.loadWord();
  2928. workingWord <<= count;
  2929. workingBitsAvailable -= count;
  2930. }
  2931. };
  2932. // (size:int):uint
  2933. this.readBits = function(size) {
  2934. var
  2935. bits = Math.min(workingBitsAvailable, size), // :uint
  2936. valu = workingWord >>> (32 - bits); // :uint
  2937. // if size > 31, handle error
  2938. workingBitsAvailable -= bits;
  2939. if (workingBitsAvailable > 0) {
  2940. workingWord <<= bits;
  2941. } else if (workingBytesAvailable > 0) {
  2942. this.loadWord();
  2943. }
  2944. bits = size - bits;
  2945. if (bits > 0) {
  2946. return valu << bits | this.readBits(bits);
  2947. }
  2948. return valu;
  2949. };
  2950. // ():uint
  2951. this.skipLeadingZeros = function() {
  2952. var leadingZeroCount; // :uint
  2953. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  2954. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  2955. // the first bit of working word is 1
  2956. workingWord <<= leadingZeroCount;
  2957. workingBitsAvailable -= leadingZeroCount;
  2958. return leadingZeroCount;
  2959. }
  2960. }
  2961. // we exhausted workingWord and still have not found a 1
  2962. this.loadWord();
  2963. return leadingZeroCount + this.skipLeadingZeros();
  2964. };
  2965. // ():void
  2966. this.skipUnsignedExpGolomb = function() {
  2967. this.skipBits(1 + this.skipLeadingZeros());
  2968. };
  2969. // ():void
  2970. this.skipExpGolomb = function() {
  2971. this.skipBits(1 + this.skipLeadingZeros());
  2972. };
  2973. // ():uint
  2974. this.readUnsignedExpGolomb = function() {
  2975. var clz = this.skipLeadingZeros(); // :uint
  2976. return this.readBits(clz + 1) - 1;
  2977. };
  2978. // ():int
  2979. this.readExpGolomb = function() {
  2980. var valu = this.readUnsignedExpGolomb(); // :int
  2981. if (0x01 & valu) {
  2982. // the number is odd if the low order bit is set
  2983. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  2984. }
  2985. return -1 * (valu >>> 1); // divide by two then make it negative
  2986. };
  2987. // Some convenience functions
  2988. // :Boolean
  2989. this.readBoolean = function() {
  2990. return this.readBits(1) === 1;
  2991. };
  2992. // ():int
  2993. this.readUnsignedByte = function() {
  2994. return this.readBits(8);
  2995. };
  2996. this.loadWord();
  2997. };
  2998. module.exports = ExpGolomb;
  2999. },{}],15:[function(require,module,exports){
  3000. /**
  3001. * mux.js
  3002. *
  3003. * Copyright (c) 2014 Brightcove
  3004. * All rights reserved.
  3005. *
  3006. * A lightweight readable stream implemention that handles event dispatching.
  3007. * Objects that inherit from streams should call init in their constructors.
  3008. */
  3009. 'use strict';
  3010. var Stream = function() {
  3011. this.init = function() {
  3012. var listeners = {};
  3013. /**
  3014. * Add a listener for a specified event type.
  3015. * @param type {string} the event name
  3016. * @param listener {function} the callback to be invoked when an event of
  3017. * the specified type occurs
  3018. */
  3019. this.on = function(type, listener) {
  3020. if (!listeners[type]) {
  3021. listeners[type] = [];
  3022. }
  3023. listeners[type] = listeners[type].concat(listener);
  3024. };
  3025. /**
  3026. * Remove a listener for a specified event type.
  3027. * @param type {string} the event name
  3028. * @param listener {function} a function previously registered for this
  3029. * type of event through `on`
  3030. */
  3031. this.off = function(type, listener) {
  3032. var index;
  3033. if (!listeners[type]) {
  3034. return false;
  3035. }
  3036. index = listeners[type].indexOf(listener);
  3037. listeners[type] = listeners[type].slice();
  3038. listeners[type].splice(index, 1);
  3039. return index > -1;
  3040. };
  3041. /**
  3042. * Trigger an event of the specified type on this stream. Any additional
  3043. * arguments to this function are passed as parameters to event listeners.
  3044. * @param type {string} the event name
  3045. */
  3046. this.trigger = function(type) {
  3047. var callbacks, i, length, args;
  3048. callbacks = listeners[type];
  3049. if (!callbacks) {
  3050. return;
  3051. }
  3052. // Slicing the arguments on every invocation of this method
  3053. // can add a significant amount of overhead. Avoid the
  3054. // intermediate object creation for the common case of a
  3055. // single callback argument
  3056. if (arguments.length === 2) {
  3057. length = callbacks.length;
  3058. for (i = 0; i < length; ++i) {
  3059. callbacks[i].call(this, arguments[1]);
  3060. }
  3061. } else {
  3062. args = [];
  3063. i = arguments.length;
  3064. for (i = 1; i < arguments.length; ++i) {
  3065. args.push(arguments[i]);
  3066. }
  3067. length = callbacks.length;
  3068. for (i = 0; i < length; ++i) {
  3069. callbacks[i].apply(this, args);
  3070. }
  3071. }
  3072. };
  3073. /**
  3074. * Destroys the stream and cleans up.
  3075. */
  3076. this.dispose = function() {
  3077. listeners = {};
  3078. };
  3079. };
  3080. };
  3081. /**
  3082. * Forwards all `data` events on this stream to the destination stream. The
  3083. * destination stream should provide a method `push` to receive the data
  3084. * events as they arrive.
  3085. * @param destination {stream} the stream that will receive all `data` events
  3086. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  3087. * when the current stream emits a 'done' event
  3088. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3089. */
  3090. Stream.prototype.pipe = function(destination) {
  3091. this.on('data', function(data) {
  3092. destination.push(data);
  3093. });
  3094. this.on('done', function(flushSource) {
  3095. destination.flush(flushSource);
  3096. });
  3097. return destination;
  3098. };
  3099. // Default stream functions that are expected to be overridden to perform
  3100. // actual work. These are provided by the prototype as a sort of no-op
  3101. // implementation so that we don't have to check for their existence in the
  3102. // `pipe` function above.
  3103. Stream.prototype.push = function(data) {
  3104. this.trigger('data', data);
  3105. };
  3106. Stream.prototype.flush = function(flushSource) {
  3107. this.trigger('done', flushSource);
  3108. };
  3109. module.exports = Stream;
  3110. },{}]},{},[6])(6)
  3111. });