Home Reference Source

src/demux/transmuxer-worker.ts

  1. import Transmuxer, { isPromise } from '../demux/transmuxer';
  2. import { Events } from '../events';
  3. import { ILogFunction, enableLogs, logger } from '../utils/logger';
  4. import { EventEmitter } from 'eventemitter3';
  5. import type { RemuxedTrack, RemuxerResult } from '../types/remuxer';
  6. import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer';
  7.  
  8. export default function TransmuxerWorker(self) {
  9. const observer = new EventEmitter();
  10. const forwardMessage = (ev, data) => {
  11. self.postMessage({ event: ev, data: data });
  12. };
  13.  
  14. // forward events to main thread
  15. observer.on(Events.FRAG_DECRYPTED, forwardMessage);
  16. observer.on(Events.ERROR, forwardMessage);
  17.  
  18. // forward logger events to main thread
  19. const forwardWorkerLogs = () => {
  20. for (const logFn in logger) {
  21. const func: ILogFunction = (message?) => {
  22. forwardMessage('workerLog', {
  23. logType: logFn,
  24. message,
  25. });
  26. };
  27.  
  28. logger[logFn] = func;
  29. }
  30. };
  31.  
  32. self.addEventListener('message', (ev) => {
  33. const data = ev.data;
  34. switch (data.cmd) {
  35. case 'init': {
  36. const config = JSON.parse(data.config);
  37. self.transmuxer = new Transmuxer(
  38. observer,
  39. data.typeSupported,
  40. config,
  41. data.vendor,
  42. data.id
  43. );
  44. enableLogs(config.debug, data.id);
  45. forwardWorkerLogs();
  46. forwardMessage('init', null);
  47. break;
  48. }
  49. case 'configure': {
  50. self.transmuxer.configure(data.config);
  51. break;
  52. }
  53. case 'demux': {
  54. const transmuxResult: TransmuxerResult | Promise<TransmuxerResult> =
  55. self.transmuxer.push(
  56. data.data,
  57. data.decryptdata,
  58. data.chunkMeta,
  59. data.state
  60. );
  61. if (isPromise(transmuxResult)) {
  62. transmuxResult.then((data) => {
  63. emitTransmuxComplete(self, data);
  64. });
  65. } else {
  66. emitTransmuxComplete(self, transmuxResult);
  67. }
  68. break;
  69. }
  70. case 'flush': {
  71. const id = data.chunkMeta;
  72. const transmuxResult = self.transmuxer.flush(id);
  73. if (isPromise(transmuxResult)) {
  74. transmuxResult.then((results: Array<TransmuxerResult>) => {
  75. handleFlushResult(self, results as Array<TransmuxerResult>, id);
  76. });
  77. } else {
  78. handleFlushResult(
  79. self,
  80. transmuxResult as Array<TransmuxerResult>,
  81. id
  82. );
  83. }
  84. break;
  85. }
  86. default:
  87. break;
  88. }
  89. });
  90. }
  91.  
  92. function emitTransmuxComplete(
  93. self: any,
  94. transmuxResult: TransmuxerResult
  95. ): boolean {
  96. if (isEmptyResult(transmuxResult.remuxResult)) {
  97. return false;
  98. }
  99. const transferable: Array<ArrayBuffer> = [];
  100. const { audio, video } = transmuxResult.remuxResult;
  101. if (audio) {
  102. addToTransferable(transferable, audio);
  103. }
  104. if (video) {
  105. addToTransferable(transferable, video);
  106. }
  107. self.postMessage(
  108. { event: 'transmuxComplete', data: transmuxResult },
  109. transferable
  110. );
  111. return true;
  112. }
  113.  
  114. // Converts data to a transferable object https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast)
  115. // in order to minimize message passing overhead
  116. function addToTransferable(
  117. transferable: Array<ArrayBuffer>,
  118. track: RemuxedTrack
  119. ) {
  120. if (track.data1) {
  121. transferable.push(track.data1.buffer);
  122. }
  123. if (track.data2) {
  124. transferable.push(track.data2.buffer);
  125. }
  126. }
  127.  
  128. function handleFlushResult(
  129. self: any,
  130. results: Array<TransmuxerResult>,
  131. chunkMeta: ChunkMetadata
  132. ) {
  133. const parsed = results.reduce(
  134. (parsed, result) => emitTransmuxComplete(self, result) || parsed,
  135. false
  136. );
  137. if (!parsed) {
  138. // Emit at least one "transmuxComplete" message even if media is not found to update stream-controller state to PARSING
  139. self.postMessage({ event: 'transmuxComplete', data: results[0] });
  140. }
  141. self.postMessage({ event: 'flush', data: chunkMeta });
  142. }
  143.  
  144. function isEmptyResult(remuxResult: RemuxerResult) {
  145. return (
  146. !remuxResult.audio &&
  147. !remuxResult.video &&
  148. !remuxResult.text &&
  149. !remuxResult.id3 &&
  150. !remuxResult.initSegment
  151. );
  152. }