@@ -79,136 +79,152 @@ A **VideoTrackWriter** converts a WritableStream of DecodedVideoFrame into a Med
7979### Example of decode for low-latency live streaming or cloud gaming   
8080
8181``` javascript 
82- const   transport   =   ... ;  //  Source of muxed/serialized messsages 
83- //  App-specific serialization/containerization code not provided  by browser 
84- const   demuxer   =   ... ;  //  Transforms to demuxed/deserialized frames 
85- const  audioBuffer   =  ... ;  //  TransformStream that buffers frames 
86- const   videoBuffer   =   ... ; 
82+ //  The app provides ReadableStreams of encoded audio and video 
83+ //  in the form of byte arrays defined  by a codec such as vp8 or opus 
84+ //  (not in a media container such as mp4 or webm). 
85+ const  { encodedAudio ,  encodedVideo }  =  ... ;
86+ //  The app also provides an element to render the decoded media 
8787const  videoElem  =  ... ;
8888
89- transport .readable .pipeTo (demuxer .writable );
89+ //  To render to an element, the ReadableStream of decoded audio and video
90+ //  must be converted to a MediaStream using TrackWriters.
91+ const  videoWriter  =  new  VideoTrackWriter ();
92+ const  audioWriter  =  new  AudioTrackWriter ();
93+ videoElem .srcObject  =  new  MediaStream ([audioWriter .track , videoWriter .track ]);
9094
95+ //  Finally the decoders are created and the encoded media is piped through the decoder
96+ //  and into the TrackerWriters which converts them into MediaStreamTracks.
9197const  audioDecoder  =  new  AudioDecoder ({codec:  ' opus' 
92- const  audioTrackWriter  =  new  AudioTrackWriter ();
93- demuxer .audio 
94-  .pipeThrough (audioBuffer)
95-  .pipeThrough (audioDecoder)
96-  .pipeTo (audioTrackWriter .writable );
97- 
9898const  videoDecoder  =  new  VideoDecoder ({codec:  ' vp8' 
99- const  videoWriter  =  new  VideoTrackWriter ();
100- demuxer .video 
101-  .pipeThrough (videoBuffer)
102-  .pipeThrough (videoDecoder)
103-  .pipeTo (videoTrackWriter .writable );
104- 
105- videoElem .srcObject  =  new  MediaStream ([audioWriter .track , videoWriter .track ]);
99+ encodedAudio .pipeThrough (audioDecoder).pipeTo (audioWriter .writable );
100+ encodedVideo .pipeThrough (videoDecoder).pipeTo (videoWriter .writable );
106101``` 
107102
108103### Example of encode for live streaming upload  
109104
110105``` javascript 
111- //  App-specific tracks, muxer, and transport
112- const  audioTrack  =  ... ;
113- const  videoTrack  =  ... ;
114- //  App-specific serialization/containerization code not provided by browser
115- const  muxer  =  ... ; //  Serializes frames for transport
116- const  transport  =  ... ; //  Sends muxed frames to server
117- 
118- const  audioTrackReader  =  new  AudioTrackReader (audioTrack);
106+ //  The app provides sources of audio and video, perhaps from getUserMedia.
107+ const  {audioTrack , videoTrack } =  ... ;
108+ //  The app also provides a way to serialize/containerize encoded media and upload it.
109+ //  The browser provides the app byte arrays defined by a codec such as vp8 or opus
110+ //  (not in a media container such as mp4 or webm).
111+ function  muxAndSend (encodedAudio , encodedVideo ) { ...  };
112+ 
113+ //  First, the tracks are converted to ReadableStreams of unencoded audio and video.
114+ const  audio  =  (new  AudioTrackReader (audioTrack)).readable ;
115+ const  video  =  (new  VideoTrackReader (videoTrack)).readable ;
116+ 
117+ //  Lastly, build the encoders and pass media through them.
119118const  audioEncoder  =  new  AudioEncoder ({
120119 codec:  ' opus' 
121120 settings:  {
122121 targetBitRate:  60_000 ,
123122 },
124123});
125- audioTrackReader .readable 
126-  .pipeThrough (audioEncoder)
127-  .pipeTo (muxer .audio );
128- 
129- const  videoTrackReader  =  new  VideoTrackReader (videoTrack);
130124const  videoEncoder  =  new  VideoEncoder ({
131125 codec:  ' vp8' 
132126 settings:  {
133127 targetBitRate:  1_000_000 
134128 },
135129});
136- videoTrackReader .readable 
137-  .pipeThrough (videoEncoder)
138-  .pipeTo (muxer .video );
139- 
140- muxer .readable .pipeTo (transport .writable );
130+ //  TODO: Example of putting dynamic settings in media flow
131+ const  encodedAudio  =  audio .pipeThrough (audioEncoder);
132+ const  encodedVideo  =  video .pipeThrough (videoEncoder);
141133
134+ muxAndSend (encodedAudio, encodedVideo);
142135``` 
143136
144137### Example of transcoding or offline encode/decode  
145138
146139``` javascript 
147- //  App-specific sources and sinks of media 
148- const  input  =  ... ; //  Reads container from source (like a file)
149- const  output  =  ... ; //  Writes container to source (like a file)
150- //  App-specific containerization code not provided by browser
151- const  demuxer  =  ... ; //  Reads container into frames
152- const  muxer  =  ... ; //  Writes frames into container
140+ //  App provides a way to demux (decontainerize) and mux (containerize) media.
141+ function  demux (input ) { ...  }
142+ function  mux (audio , video ) { ...  }
143+ const  input  =  ... ;
153144
154145const  audioDecoder  =  new  AudioDecoder ({codec:  ' aac' 
146+ const  videoDecoder  =  new  VideoDecoder ({codec:  ' h264' 
147+ 
155148const  audioEncoder  =  new  AudioEncoder ({
156149 codec:  ' opus' 
157150 settings:  {
158151 targetBitRate:  60_000 ,
159152 },
160153});
161- demuxer .audio 
162-  .pipeThrough (audioDecoder)
163-  .pipeThrough (audioEncoder)
164-  .pipeTo (muxer .audio );
165- 
166- const  videoDecoder  =  new  VideoDecoder ({codec:  ' h264' 
167154const  videoEncoder  =  new  VideoEncoder ({
168155 codec:  ' vp8' 
169156 settings:  {
170157 bitsPerSecond:  1_000_000 ,
171158 },
172159});
173- demuxer .video 
174-  .pipeThrough (videoDecoder)
175-  .pipeThrough (videoEncoder)
176-  .pipeTo (muxer .video );
177160
178- input .readable .pipeInto (demuxer .writable );
179- muxer .readable .pipeInto (output .writable );
161+ const  {audioIn , videoIn } =  demux (input);
162+ const  audioOut  =  audioIn .pipeThrough (audioDecoder).pipeThrough (audioEncoder);
163+ const  videoOut  =  videoIn .pipeThrough (videoDecoder).pipeThrough (videoEncoder);
164+ const  output  =  mux (audioOut, videoOut);
165+ 
180166``` 
181167
182- ### Example of advanced  real-time communication  
168+ ### Example of real-time communication  
183169
184170``` javascript 
185- //  Sender has  app-specific encryptor  and transport 
186- const  audioTrack  =  ... ;
187- const   videoTrack   =   ... ; 
188- const   audioEncryptor   =   ... ;  //  TransformStream that encrypts encoded media 
189- const   videoEncryptor   =   ... ; 
190- //  App-specific containerization code not provided by browser 
191- const  muxer  =  ... ;  //  Transforms frames into muxed messages 
192- const  transport  =  ... ;  //  Sink of encrypted, muxed messages 
193- 
194- const   audioTrackReader   =   new   AudioTrackReader (audioTrack); 
171+ //  The  app provides sources of audio  and video, perhaps from getUserMedia. 
172+ const  { audioTrack ,  videoTrack }  =  ... ;
173+ //  The app also provides ways to send encoded audio and video bitstream. 
174+ function   sendMedia ( encodedAudio ,  encodedVideo ) {  ...  }; 
175+ 
176+ //  First, the tracks are converted to ReadableStreams of framed bitstream chunks. 
177+ const  audio  =  ( new   AudioTrackReader (audioTrack)). readable ; 
178+ const  video  =  ( new   VideoTrackReader (videoTrack)). readable ; 
179+ 
180+ //  Next, build the encoders and pass media through them 
195181const  audioEncoder  =  new  AudioEncoder ({
196-  codec:  ' opus'   
182+  codec:  ' opus' 
197183 settings:  {
198184 targetBitRate:  60_000 ,
199185 },
200186});
201- audioTrackReader .readable 
202-  .pipeThrough (audioEncoder)
203-  .pipeThrough (audioEncryptor)
204-  .pipeThrough (muxer)
205-  .pipeTo (transport .writable );
187+ const  videoEncoder  =  new  VideoEncoder ({
188+  codec:  ' vp8' 
189+  settings:  {
190+  bitsPerSecond:  1_000_000 ,
191+  },
192+ });
193+ //  TODO: Example of putting dynamic settings in media flow
194+ const  encodedAudio  =  audio .pipeThrough (audioEncoder);
195+ const  encodedVideo  =  video .pipeThrough (videoEncoder);
196+ 
197+ //  Then send the encoded audio and video
198+ sendMedia (encodedAudio, encodedVideo);
199+ 
200+ //  On the receive side, encoded media is likely received
201+ //  from an out-of-order p2p transport and then put into a buffer.
202+ //  The output of that buffer is the source of encoded audio and video here.
203+ const  {encodedAudio , encodedVideo } =  ... ;
204+ 
205+ //  To render to an element, the ReadableStream of decoded audio and video
206+ //  must be converted to a MediaStream using TrackWriters.
207+ const  videoWriter  =  new  VideoTrackWriter ();
208+ const  audioWriter  =  new  AudioTrackWriter ();
209+ videoElem .srcObject  =  new  MediaStream ([audioWriter .track , videoWriter .track ]);
210+ 
211+ //  Finally the decoders are created and the encoded media is piped through the decoder
212+ //  and into the TrackerWriters which converts them into MediaStreamTracks.
213+ const  audioDecoder  =  new  AudioDecoder ({codec:  ' opus' 
214+ const  videoDecoder  =  new  VideoDecoder ({codec:  ' vp8' 
215+ encodedAudio .pipeThrough (audioDecoder).pipeTo (audioWriter .writable );
216+ encodedVideo .pipeThrough (videoDecoder).pipeTo (videoWriter .writable );
217+ ``` 
218+ 
219+ ### Example of real-time communication using SVC  
206220
207- const  videoTrackReader  =  new  VideoTrackReader (videoTrack);
221+ The same as above, but with fancier codec parameters:
222+ 
223+ ``` javascript 
208224const  videoEncoder  =  new  VideoEncoder ({
209-  codec:  ' vp9'   
225+  codec:  ' vp9' 
210226 settings:  {
211-  bitsPerSecond:  1000000 ,
227+  bitsPerSecond:  1_000_000 ,
212228 //  Two spatial layers with two temporal layers each
213229 layers:  [{
214230 //  Quarter size base layer
@@ -232,83 +248,6 @@ const videoEncoder = new VideoEncoder({
232248 }],
233249 },
234250});
235- videoTrackReader .readable 
236-  .pipeThrough (videoEncoder)
237-  .pipeThrough (videoEncryptor)
238-  .pipeThrough (muxer .video )
239- 
240- muxer .readable .pipeTo (transport .writable );
241- 
242- 
243- //  Receiver has app-specific decryptor and buffering behavior
244- const  transport  =  ... ; //  Source of encrypted, muxed messsages
245- //  App-specific containerization code not provided by browser
246- const  demuxer  =  ... ; //  Transforms muxed messages to demuxed frames
247- const  audioDecryptor  =  ... ; //  TransformStream that decrypts frames
248- const  videoDecryptor  =  ... ;
249- const  audioBuffer  =  ... ; //  TransformStream that buffers frames
250- const  videoBuffer  =  ... ;
251- const  videoElem  =  ... ;
252- 
253- transport .readable .pipeTo (demuxer .writable );
254- 
255- const  audioDecoder  =  new  AudioDecoder ({codec:  ' opus' 
256- const  audioTrackWriter  =  new  AudioTrackWriter ();
257- demuxer .audio 
258-  .pipeThrough (audioDecryptor)
259-  .pipeThrough (audioBuffer)
260-  .pipeThrough (audioDecoder)
261-  .pipeTo (audioTrackWriter .writable );
262- 
263- const  videoDecoder  =  new  videoDecoder ({codec:  ' vp8' 
264- const  videoWriter  =  new  VideoTrackWriter ();
265- demuxer .video 
266-  .pipeThrough (videoDecryptor)
267-  .pipeThrough (videoBuffer)
268-  .pipeThrough (videoDecoder)
269-  .pipeTo (videoTrackWriter .writable );
270- 
271- videoElem .srcObject  =  new  MediaStream ([audioWriter .track , videoWriter .track ]);
272- 
273- ``` 
274- 
275- ### Example of transcoding or offline encode/decode  
276- 
277- ``` javascript 
278- //  App-specific sources and sinks of media
279- const  input  =  ... ; //  Reads container from source (like a file)
280- const  output  =  ... ; //  Writes container to source (like a file)
281- //  App-specific containerization code (not provided by browser)
282- const  demuxer  =  ... ; //  Reads container into frames
283- const  muxer  =  ... ; //  Writes frames into container
284- 
285- 
286- const  audioDecoder  =  new  AudioDecoder ({codec:  ' aac' 
287- const  audioEncoder  =  new  AudioEncoder ({
288-  codec:  ' opus' 
289-  settings:  {
290-  targetBitRate:  60_000 
291-  },
292- });
293- demuxer .audio 
294-  .pipeThrough (audioDecoder)
295-  .pipeThrough (audioEncoder)
296-  .pipeTo (muxer .audio );
297- 
298- const  videoDecoder  =  new  VideoDecoder ({codec:  ' h264' 
299- const  videoEncoder  =  new  VideoEncoder ({
300-  codec:  ' vp8' 
301-  settings:  {
302-  bitsPerSecond:  1_000_000 ,
303-  },
304- });
305- demuxer .video 
306-  .pipeThrough (videoDecoder)
307-  .pipeThrough (videoEncoder)
308-  .pipeTo (muxer .video );
309- 
310- input .readable .pipeInto (demuxer .writable );
311- muxer .readable .pipeInto (output .writable );
312251``` 
313252
314253## Detailed design discussion  
0 commit comments