|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 // Original author: ekr@rtfm.com |
|
7 |
|
8 #ifndef mediapipeline_h__ |
|
9 #define mediapipeline_h__ |
|
10 |
|
11 #include "sigslot.h" |
|
12 |
|
13 #ifdef USE_FAKE_MEDIA_STREAMS |
|
14 #include "FakeMediaStreams.h" |
|
15 #else |
|
16 #include "DOMMediaStream.h" |
|
17 #include "MediaStreamGraph.h" |
|
18 #include "VideoUtils.h" |
|
19 #endif |
|
20 #include "MediaConduitInterface.h" |
|
21 #include "MediaPipelineFilter.h" |
|
22 #include "AudioSegment.h" |
|
23 #include "mozilla/ReentrantMonitor.h" |
|
24 #include "SrtpFlow.h" |
|
25 #include "databuffer.h" |
|
26 #include "runnable_utils.h" |
|
27 #include "transportflow.h" |
|
28 |
|
29 #ifdef MOZILLA_INTERNAL_API |
|
30 #include "VideoSegment.h" |
|
31 #endif |
|
32 |
|
33 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" |
|
34 |
|
35 namespace mozilla { |
|
36 |
|
37 // A class that represents the pipeline of audio and video |
|
38 // The dataflow looks like: |
|
39 // |
|
40 // TRANSMIT |
|
41 // CaptureDevice -> stream -> [us] -> conduit -> [us] -> transport -> network |
|
42 // |
|
43 // RECEIVE |
|
44 // network -> transport -> [us] -> conduit -> [us] -> stream -> Playout |
|
45 // |
|
46 // The boxes labeled [us] are just bridge logic implemented in this class |
|
47 // |
|
48 // We have to deal with a number of threads: |
|
49 // |
|
50 // GSM: |
|
51 // * Assembles the pipeline |
|
52 // SocketTransportService |
|
53 // * Receives notification that ICE and DTLS have completed |
|
54 // * Processes incoming network data and passes it to the conduit |
|
55 // * Processes outgoing RTP and RTCP |
|
56 // MediaStreamGraph |
|
57 // * Receives outgoing data from the MediaStreamGraph |
|
58 // * Receives pull requests for more data from the |
|
59 // MediaStreamGraph |
|
60 // One or another GIPS threads |
|
61 // * Receives RTCP messages to send to the other side |
|
62 // * Processes video frames GIPS wants to render |
|
63 // |
|
64 // For a transmitting conduit, "output" is RTP and "input" is RTCP. |
|
65 // For a receiving conduit, "input" is RTP and "output" is RTCP. |
|
66 // |
|
67 class MediaPipeline : public sigslot::has_slots<> { |
|
68 public: |
|
69 enum Direction { TRANSMIT, RECEIVE }; |
|
70 enum State { MP_CONNECTING, MP_OPEN, MP_CLOSED }; |
|
71 MediaPipeline(const std::string& pc, |
|
72 Direction direction, |
|
73 nsCOMPtr<nsIEventTarget> main_thread, |
|
74 nsCOMPtr<nsIEventTarget> sts_thread, |
|
75 MediaStream *stream, |
|
76 TrackID track_id, |
|
77 int level, |
|
78 RefPtr<MediaSessionConduit> conduit, |
|
79 RefPtr<TransportFlow> rtp_transport, |
|
80 RefPtr<TransportFlow> rtcp_transport) |
|
81 : direction_(direction), |
|
82 stream_(stream), |
|
83 track_id_(track_id), |
|
84 level_(level), |
|
85 conduit_(conduit), |
|
86 rtp_(rtp_transport, rtcp_transport ? RTP : MUX), |
|
87 rtcp_(rtcp_transport ? rtcp_transport : rtp_transport, |
|
88 rtcp_transport ? RTCP : MUX), |
|
89 main_thread_(main_thread), |
|
90 sts_thread_(sts_thread), |
|
91 rtp_packets_sent_(0), |
|
92 rtcp_packets_sent_(0), |
|
93 rtp_packets_received_(0), |
|
94 rtcp_packets_received_(0), |
|
95 rtp_bytes_sent_(0), |
|
96 rtp_bytes_received_(0), |
|
97 pc_(pc), |
|
98 description_() { |
|
99 // To indicate rtcp-mux rtcp_transport should be nullptr. |
|
100 // Therefore it's an error to send in the same flow for |
|
101 // both rtp and rtcp. |
|
102 MOZ_ASSERT(rtp_transport != rtcp_transport); |
|
103 |
|
104 // PipelineTransport() will access this->sts_thread_; moved here for safety |
|
105 transport_ = new PipelineTransport(this); |
|
106 } |
|
107 |
|
108 virtual ~MediaPipeline(); |
|
109 |
|
110 // Must be called on the STS thread. Must be called after ShutdownMedia_m(). |
|
111 void ShutdownTransport_s(); |
|
112 |
|
113 // Must be called on the main thread. |
|
114 void ShutdownMedia_m() { |
|
115 ASSERT_ON_THREAD(main_thread_); |
|
116 |
|
117 if (stream_) { |
|
118 DetachMediaStream(); |
|
119 } |
|
120 } |
|
121 |
|
122 virtual nsresult Init(); |
|
123 |
|
124 // When we have offered bundle, the MediaPipelines are created in an |
|
125 // indeterminate state; we do not know whether the answerer will take us |
|
126 // up on our offer. In the meantime, we need to behave in a manner that |
|
127 // errs on the side of packet loss when it is unclear whether an arriving |
|
128 // packet is meant for us. We want to get out of this indeterminate state |
|
129 // ASAP, which is what this function can be used for. |
|
130 void SetUsingBundle_s(bool decision); |
|
131 MediaPipelineFilter* UpdateFilterFromRemoteDescription_s( |
|
132 nsAutoPtr<MediaPipelineFilter> filter); |
|
133 |
|
134 virtual Direction direction() const { return direction_; } |
|
135 virtual TrackID trackid() const { return track_id_; } |
|
136 virtual int level() const { return level_; } |
|
137 |
|
138 bool IsDoingRtcpMux() const { |
|
139 return (rtp_.type_ == MUX); |
|
140 } |
|
141 |
|
142 int32_t rtp_packets_sent() const { return rtp_packets_sent_; } |
|
143 int64_t rtp_bytes_sent() const { return rtp_bytes_sent_; } |
|
144 int32_t rtcp_packets_sent() const { return rtcp_packets_sent_; } |
|
145 int32_t rtp_packets_received() const { return rtp_packets_received_; } |
|
146 int64_t rtp_bytes_received() const { return rtp_bytes_received_; } |
|
147 int32_t rtcp_packets_received() const { return rtcp_packets_received_; } |
|
148 |
|
149 MediaSessionConduit *Conduit() const { return conduit_; } |
|
150 |
|
151 // Thread counting |
|
152 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) |
|
153 |
|
154 typedef enum { |
|
155 RTP, |
|
156 RTCP, |
|
157 MUX, |
|
158 MAX_RTP_TYPE |
|
159 } RtpType; |
|
160 |
|
161 protected: |
|
162 virtual void DetachMediaStream() {} |
|
163 |
|
164 // Separate class to allow ref counting |
|
165 class PipelineTransport : public TransportInterface { |
|
166 public: |
|
167 // Implement the TransportInterface functions |
|
168 explicit PipelineTransport(MediaPipeline *pipeline) |
|
169 : pipeline_(pipeline), |
|
170 sts_thread_(pipeline->sts_thread_) {} |
|
171 |
|
172 void Detach() { pipeline_ = nullptr; } |
|
173 MediaPipeline *pipeline() const { return pipeline_; } |
|
174 |
|
175 virtual nsresult SendRtpPacket(const void* data, int len); |
|
176 virtual nsresult SendRtcpPacket(const void* data, int len); |
|
177 |
|
178 private: |
|
179 virtual nsresult SendRtpPacket_s(nsAutoPtr<DataBuffer> data); |
|
180 virtual nsresult SendRtcpPacket_s(nsAutoPtr<DataBuffer> data); |
|
181 |
|
182 MediaPipeline *pipeline_; // Raw pointer to avoid cycles |
|
183 nsCOMPtr<nsIEventTarget> sts_thread_; |
|
184 }; |
|
185 friend class PipelineTransport; |
|
186 |
|
187 class TransportInfo { |
|
188 public: |
|
189 TransportInfo(RefPtr<TransportFlow> flow, RtpType type) : |
|
190 transport_(flow), |
|
191 state_(MP_CONNECTING), |
|
192 type_(type) { |
|
193 MOZ_ASSERT(flow); |
|
194 } |
|
195 |
|
196 RefPtr<TransportFlow> transport_; |
|
197 State state_; |
|
198 RefPtr<SrtpFlow> send_srtp_; |
|
199 RefPtr<SrtpFlow> recv_srtp_; |
|
200 RtpType type_; |
|
201 }; |
|
202 |
|
203 // The transport is down |
|
204 virtual nsresult TransportFailed_s(TransportInfo &info); |
|
205 // The transport is ready |
|
206 virtual nsresult TransportReady_s(TransportInfo &info); |
|
207 void UpdateRtcpMuxState(TransportInfo &info); |
|
208 |
|
209 // Unhooks from signals |
|
210 void DisconnectTransport_s(TransportInfo &info); |
|
211 nsresult ConnectTransport_s(TransportInfo &info); |
|
212 |
|
213 TransportInfo* GetTransportInfo_s(TransportFlow *flow); |
|
214 |
|
215 void increment_rtp_packets_sent(int bytes); |
|
216 void increment_rtcp_packets_sent(); |
|
217 void increment_rtp_packets_received(int bytes); |
|
218 void increment_rtcp_packets_received(); |
|
219 |
|
220 virtual nsresult SendPacket(TransportFlow *flow, const void *data, int len); |
|
221 |
|
222 // Process slots on transports |
|
223 void StateChange(TransportFlow *flow, TransportLayer::State); |
|
224 void RtpPacketReceived(TransportLayer *layer, const unsigned char *data, |
|
225 size_t len); |
|
226 void RtcpPacketReceived(TransportLayer *layer, const unsigned char *data, |
|
227 size_t len); |
|
228 void PacketReceived(TransportLayer *layer, const unsigned char *data, |
|
229 size_t len); |
|
230 |
|
231 Direction direction_; |
|
232 RefPtr<MediaStream> stream_; // A pointer to the stream we are servicing. |
|
233 // Written on the main thread. |
|
234 // Used on STS and MediaStreamGraph threads. |
|
235 TrackID track_id_; // The track on the stream. |
|
236 // Written and used as the stream_; |
|
237 int level_; // The m-line index (starting at 1, to match convention) |
|
238 RefPtr<MediaSessionConduit> conduit_; // Our conduit. Written on the main |
|
239 // thread. Read on STS thread. |
|
240 |
|
241 // The transport objects. Read/written on STS thread. |
|
242 TransportInfo rtp_; |
|
243 TransportInfo rtcp_; |
|
244 // These are for bundle. We have a separate set because when we have offered |
|
245 // bundle, we do not know whether we will receive traffic on the transport |
|
246 // in this pipeline's m-line, or the transport in the "master" m-line for |
|
247 // the bundle. We need to be ready for either. Once this ambiguity is |
|
248 // resolved, the transport we know that we'll be using will be set in |
|
249 // rtp_transport_ and rtcp_transport_, and these will be unset. |
|
250 // TODO(bcampen@mozilla.com): I'm pretty sure this could be leveraged for |
|
251 // re-offer with a new address on an m-line too, with a little work. |
|
252 nsAutoPtr<TransportInfo> possible_bundle_rtp_; |
|
253 nsAutoPtr<TransportInfo> possible_bundle_rtcp_; |
|
254 |
|
255 // Pointers to the threads we need. Initialized at creation |
|
256 // and used all over the place. |
|
257 nsCOMPtr<nsIEventTarget> main_thread_; |
|
258 nsCOMPtr<nsIEventTarget> sts_thread_; |
|
259 |
|
260 // Created on Init. Referenced by the conduit and eventually |
|
261 // destroyed on the STS thread. |
|
262 RefPtr<PipelineTransport> transport_; |
|
263 |
|
264 // Only safe to access from STS thread. |
|
265 // Build into TransportInfo? |
|
266 int32_t rtp_packets_sent_; |
|
267 int32_t rtcp_packets_sent_; |
|
268 int32_t rtp_packets_received_; |
|
269 int32_t rtcp_packets_received_; |
|
270 int64_t rtp_bytes_sent_; |
|
271 int64_t rtp_bytes_received_; |
|
272 |
|
273 // Written on Init. Read on STS thread. |
|
274 std::string pc_; |
|
275 std::string description_; |
|
276 |
|
277 // Written on Init, all following accesses are on the STS thread. |
|
278 nsAutoPtr<MediaPipelineFilter> filter_; |
|
279 nsAutoPtr<webrtc::RtpHeaderParser> rtp_parser_; |
|
280 |
|
281 private: |
|
282 nsresult Init_s(); |
|
283 |
|
284 bool IsRtp(const unsigned char *data, size_t len); |
|
285 }; |
|
286 |
|
287 class GenericReceiveListener : public MediaStreamListener |
|
288 { |
|
289 public: |
|
290 GenericReceiveListener(SourceMediaStream *source, TrackID track_id, |
|
291 TrackRate track_rate) |
|
292 : source_(source), |
|
293 track_id_(track_id), |
|
294 track_rate_(track_rate), |
|
295 played_ticks_(0) {} |
|
296 |
|
297 virtual ~GenericReceiveListener() {} |
|
298 |
|
299 void AddSelf(MediaSegment* segment); |
|
300 |
|
301 void SetPlayedTicks(TrackTicks time) { |
|
302 played_ticks_ = time; |
|
303 } |
|
304 |
|
305 void EndTrack() { |
|
306 source_->EndTrack(track_id_); |
|
307 } |
|
308 |
|
309 protected: |
|
310 SourceMediaStream *source_; |
|
311 TrackID track_id_; |
|
312 TrackRate track_rate_; |
|
313 TrackTicks played_ticks_; |
|
314 }; |
|
315 |
|
316 class TrackAddedCallback { |
|
317 public: |
|
318 virtual void TrackAdded(TrackTicks current_ticks) = 0; |
|
319 |
|
320 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackAddedCallback); |
|
321 |
|
322 protected: |
|
323 virtual ~TrackAddedCallback() {} |
|
324 }; |
|
325 |
|
326 class GenericReceiveListener; |
|
327 |
|
328 class GenericReceiveCallback : public TrackAddedCallback |
|
329 { |
|
330 public: |
|
331 GenericReceiveCallback(GenericReceiveListener* listener) |
|
332 : listener_(listener) {} |
|
333 |
|
334 void TrackAdded(TrackTicks time) { |
|
335 listener_->SetPlayedTicks(time); |
|
336 } |
|
337 |
|
338 private: |
|
339 RefPtr<GenericReceiveListener> listener_; |
|
340 }; |
|
341 |
|
342 class ConduitDeleteEvent: public nsRunnable |
|
343 { |
|
344 public: |
|
345 ConduitDeleteEvent(TemporaryRef<MediaSessionConduit> aConduit) : |
|
346 mConduit(aConduit) {} |
|
347 |
|
348 /* we exist solely to proxy release of the conduit */ |
|
349 NS_IMETHOD Run() { return NS_OK; } |
|
350 private: |
|
351 RefPtr<MediaSessionConduit> mConduit; |
|
352 }; |
|
353 |
|
354 // A specialization of pipeline for reading from an input device |
|
355 // and transmitting to the network. |
|
356 class MediaPipelineTransmit : public MediaPipeline { |
|
357 public: |
|
358 // Set rtcp_transport to nullptr to use rtcp-mux |
|
359 MediaPipelineTransmit(const std::string& pc, |
|
360 nsCOMPtr<nsIEventTarget> main_thread, |
|
361 nsCOMPtr<nsIEventTarget> sts_thread, |
|
362 DOMMediaStream *domstream, |
|
363 TrackID track_id, |
|
364 int level, |
|
365 RefPtr<MediaSessionConduit> conduit, |
|
366 RefPtr<TransportFlow> rtp_transport, |
|
367 RefPtr<TransportFlow> rtcp_transport) : |
|
368 MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, |
|
369 domstream->GetStream(), track_id, level, |
|
370 conduit, rtp_transport, rtcp_transport), |
|
371 listener_(new PipelineListener(conduit)), |
|
372 domstream_(domstream) |
|
373 {} |
|
374 |
|
375 // Initialize (stuff here may fail) |
|
376 virtual nsresult Init(); |
|
377 |
|
378 // Called on the main thread. |
|
379 virtual void DetachMediaStream() { |
|
380 ASSERT_ON_THREAD(main_thread_); |
|
381 domstream_->RemoveDirectListener(listener_); |
|
382 domstream_ = nullptr; |
|
383 stream_->RemoveListener(listener_); |
|
384 // Let the listener be destroyed with the pipeline (or later). |
|
385 stream_ = nullptr; |
|
386 } |
|
387 |
|
388 // Override MediaPipeline::TransportReady. |
|
389 virtual nsresult TransportReady_s(TransportInfo &info); |
|
390 |
|
391 // Separate class to allow ref counting |
|
392 class PipelineListener : public MediaStreamDirectListener { |
|
393 friend class MediaPipelineTransmit; |
|
394 public: |
|
395 PipelineListener(const RefPtr<MediaSessionConduit>& conduit) |
|
396 : conduit_(conduit), |
|
397 active_(false), |
|
398 direct_connect_(false), |
|
399 samples_10ms_buffer_(nullptr), |
|
400 buffer_current_(0), |
|
401 samplenum_10ms_(0) |
|
402 #ifdef MOZILLA_INTERNAL_API |
|
403 , last_img_(-1) |
|
404 #endif // MOZILLA_INTERNAL_API |
|
405 { |
|
406 } |
|
407 |
|
408 ~PipelineListener() |
|
409 { |
|
410 // release conduit on mainthread. Must use forget()! |
|
411 nsresult rv = NS_DispatchToMainThread(new |
|
412 ConduitDeleteEvent(conduit_.forget()), NS_DISPATCH_NORMAL); |
|
413 MOZ_ASSERT(!NS_FAILED(rv),"Could not dispatch conduit shutdown to main"); |
|
414 if (NS_FAILED(rv)) { |
|
415 MOZ_CRASH(); |
|
416 } |
|
417 } |
|
418 |
|
419 |
|
420 // XXX. This is not thread-safe but the hazard is just |
|
421 // that active_ = true takes a while to propagate. Revisit |
|
422 // when 823600 lands. |
|
423 void SetActive(bool active) { active_ = active; } |
|
424 |
|
425 // Implement MediaStreamListener |
|
426 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, |
|
427 TrackRate rate, |
|
428 TrackTicks offset, |
|
429 uint32_t events, |
|
430 const MediaSegment& queued_media) MOZ_OVERRIDE; |
|
431 virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) MOZ_OVERRIDE {} |
|
432 |
|
433 // Implement MediaStreamDirectListener |
|
434 virtual void NotifyRealtimeData(MediaStreamGraph* graph, TrackID tid, |
|
435 TrackRate rate, |
|
436 TrackTicks offset, |
|
437 uint32_t events, |
|
438 const MediaSegment& media) MOZ_OVERRIDE; |
|
439 |
|
440 private: |
|
441 void NewData(MediaStreamGraph* graph, TrackID tid, |
|
442 TrackRate rate, |
|
443 TrackTicks offset, |
|
444 uint32_t events, |
|
445 const MediaSegment& media); |
|
446 |
|
447 virtual void ProcessAudioChunk(AudioSessionConduit *conduit, |
|
448 TrackRate rate, AudioChunk& chunk); |
|
449 #ifdef MOZILLA_INTERNAL_API |
|
450 virtual void ProcessVideoChunk(VideoSessionConduit *conduit, |
|
451 TrackRate rate, VideoChunk& chunk); |
|
452 #endif |
|
453 RefPtr<MediaSessionConduit> conduit_; |
|
454 volatile bool active_; |
|
455 bool direct_connect_; |
|
456 |
|
457 // These vars handle breaking audio samples into exact 10ms chunks: |
|
458 // The buffer of 10ms audio samples that we will send once full |
|
459 // (can be carried over from one call to another). |
|
460 nsAutoArrayPtr<int16_t> samples_10ms_buffer_; |
|
461 // The location of the pointer within that buffer (in units of samples). |
|
462 int64_t buffer_current_; |
|
463 // The number of samples in a 10ms audio chunk. |
|
464 int64_t samplenum_10ms_; |
|
465 |
|
466 #ifdef MOZILLA_INTERNAL_API |
|
467 int32_t last_img_; // serial number of last Image |
|
468 #endif // MOZILLA_INTERNAL_API |
|
469 }; |
|
470 |
|
471 private: |
|
472 RefPtr<PipelineListener> listener_; |
|
473 DOMMediaStream *domstream_; |
|
474 }; |
|
475 |
|
476 |
|
477 // A specialization of pipeline for reading from the network and |
|
478 // rendering video. |
|
479 class MediaPipelineReceive : public MediaPipeline { |
|
480 public: |
|
481 // Set rtcp_transport to nullptr to use rtcp-mux |
|
482 MediaPipelineReceive(const std::string& pc, |
|
483 nsCOMPtr<nsIEventTarget> main_thread, |
|
484 nsCOMPtr<nsIEventTarget> sts_thread, |
|
485 MediaStream *stream, |
|
486 TrackID track_id, |
|
487 int level, |
|
488 RefPtr<MediaSessionConduit> conduit, |
|
489 RefPtr<TransportFlow> rtp_transport, |
|
490 RefPtr<TransportFlow> rtcp_transport, |
|
491 RefPtr<TransportFlow> bundle_rtp_transport, |
|
492 RefPtr<TransportFlow> bundle_rtcp_transport, |
|
493 nsAutoPtr<MediaPipelineFilter> filter) : |
|
494 MediaPipeline(pc, RECEIVE, main_thread, sts_thread, |
|
495 stream, track_id, level, conduit, rtp_transport, |
|
496 rtcp_transport), |
|
497 segments_added_(0) { |
|
498 filter_ = filter; |
|
499 rtp_parser_ = webrtc::RtpHeaderParser::Create(); |
|
500 if (bundle_rtp_transport) { |
|
501 if (bundle_rtcp_transport) { |
|
502 MOZ_ASSERT(bundle_rtp_transport != bundle_rtcp_transport); |
|
503 possible_bundle_rtp_ = new TransportInfo(bundle_rtp_transport, RTP); |
|
504 possible_bundle_rtcp_ = new TransportInfo(bundle_rtcp_transport, RTCP); |
|
505 } else { |
|
506 possible_bundle_rtp_ = new TransportInfo(bundle_rtp_transport, MUX); |
|
507 possible_bundle_rtcp_ = new TransportInfo(bundle_rtp_transport, MUX); |
|
508 } |
|
509 } |
|
510 } |
|
511 |
|
512 int segments_added() const { return segments_added_; } |
|
513 |
|
514 protected: |
|
515 int segments_added_; |
|
516 |
|
517 private: |
|
518 }; |
|
519 |
|
520 |
|
521 // A specialization of pipeline for reading from the network and |
|
522 // rendering audio. |
|
523 class MediaPipelineReceiveAudio : public MediaPipelineReceive { |
|
524 public: |
|
525 MediaPipelineReceiveAudio(const std::string& pc, |
|
526 nsCOMPtr<nsIEventTarget> main_thread, |
|
527 nsCOMPtr<nsIEventTarget> sts_thread, |
|
528 MediaStream *stream, |
|
529 TrackID track_id, |
|
530 int level, |
|
531 RefPtr<AudioSessionConduit> conduit, |
|
532 RefPtr<TransportFlow> rtp_transport, |
|
533 RefPtr<TransportFlow> rtcp_transport, |
|
534 RefPtr<TransportFlow> bundle_rtp_transport, |
|
535 RefPtr<TransportFlow> bundle_rtcp_transport, |
|
536 nsAutoPtr<MediaPipelineFilter> filter) : |
|
537 MediaPipelineReceive(pc, main_thread, sts_thread, |
|
538 stream, track_id, level, conduit, rtp_transport, |
|
539 rtcp_transport, bundle_rtp_transport, |
|
540 bundle_rtcp_transport, filter), |
|
541 listener_(new PipelineListener(stream->AsSourceStream(), |
|
542 track_id, conduit)) { |
|
543 } |
|
544 |
|
545 virtual void DetachMediaStream() { |
|
546 ASSERT_ON_THREAD(main_thread_); |
|
547 listener_->EndTrack(); |
|
548 stream_->RemoveListener(listener_); |
|
549 stream_ = nullptr; |
|
550 } |
|
551 |
|
552 virtual nsresult Init(); |
|
553 |
|
554 private: |
|
555 // Separate class to allow ref counting |
|
556 class PipelineListener : public GenericReceiveListener { |
|
557 public: |
|
558 PipelineListener(SourceMediaStream * source, TrackID track_id, |
|
559 const RefPtr<MediaSessionConduit>& conduit); |
|
560 |
|
561 ~PipelineListener() |
|
562 { |
|
563 // release conduit on mainthread. Must use forget()! |
|
564 nsresult rv = NS_DispatchToMainThread(new |
|
565 ConduitDeleteEvent(conduit_.forget()), NS_DISPATCH_NORMAL); |
|
566 MOZ_ASSERT(!NS_FAILED(rv),"Could not dispatch conduit shutdown to main"); |
|
567 if (NS_FAILED(rv)) { |
|
568 MOZ_CRASH(); |
|
569 } |
|
570 } |
|
571 |
|
572 // Implement MediaStreamListener |
|
573 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, |
|
574 TrackRate rate, |
|
575 TrackTicks offset, |
|
576 uint32_t events, |
|
577 const MediaSegment& queued_media) MOZ_OVERRIDE {} |
|
578 virtual void NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) MOZ_OVERRIDE; |
|
579 |
|
580 private: |
|
581 RefPtr<MediaSessionConduit> conduit_; |
|
582 }; |
|
583 |
|
584 RefPtr<PipelineListener> listener_; |
|
585 }; |
|
586 |
|
587 |
|
588 // A specialization of pipeline for reading from the network and |
|
589 // rendering video. |
|
590 class MediaPipelineReceiveVideo : public MediaPipelineReceive { |
|
591 public: |
|
592 MediaPipelineReceiveVideo(const std::string& pc, |
|
593 nsCOMPtr<nsIEventTarget> main_thread, |
|
594 nsCOMPtr<nsIEventTarget> sts_thread, |
|
595 MediaStream *stream, |
|
596 TrackID track_id, |
|
597 int level, |
|
598 RefPtr<VideoSessionConduit> conduit, |
|
599 RefPtr<TransportFlow> rtp_transport, |
|
600 RefPtr<TransportFlow> rtcp_transport, |
|
601 RefPtr<TransportFlow> bundle_rtp_transport, |
|
602 RefPtr<TransportFlow> bundle_rtcp_transport, |
|
603 nsAutoPtr<MediaPipelineFilter> filter) : |
|
604 MediaPipelineReceive(pc, main_thread, sts_thread, |
|
605 stream, track_id, level, conduit, rtp_transport, |
|
606 rtcp_transport, bundle_rtp_transport, |
|
607 bundle_rtcp_transport, filter), |
|
608 renderer_(new PipelineRenderer(MOZ_THIS_IN_INITIALIZER_LIST())), |
|
609 listener_(new PipelineListener(stream->AsSourceStream(), track_id)) { |
|
610 } |
|
611 |
|
612 // Called on the main thread. |
|
613 virtual void DetachMediaStream() { |
|
614 ASSERT_ON_THREAD(main_thread_); |
|
615 |
|
616 listener_->EndTrack(); |
|
617 // stop generating video and thus stop invoking the PipelineRenderer |
|
618 // and PipelineListener - the renderer has a raw ptr to the Pipeline to |
|
619 // avoid cycles, and the render callbacks are invoked from a different |
|
620 // thread so simple null-checks would cause TSAN bugs without locks. |
|
621 static_cast<VideoSessionConduit*>(conduit_.get())->DetachRenderer(); |
|
622 stream_->RemoveListener(listener_); |
|
623 stream_ = nullptr; |
|
624 } |
|
625 |
|
626 virtual nsresult Init(); |
|
627 |
|
628 private: |
|
629 class PipelineRenderer : public VideoRenderer { |
|
630 public: |
|
631 PipelineRenderer(MediaPipelineReceiveVideo *pipeline) : |
|
632 pipeline_(pipeline) {} |
|
633 |
|
634 void Detach() { pipeline_ = nullptr; } |
|
635 |
|
636 // Implement VideoRenderer |
|
637 virtual void FrameSizeChange(unsigned int width, |
|
638 unsigned int height, |
|
639 unsigned int number_of_streams) { |
|
640 pipeline_->listener_->FrameSizeChange(width, height, number_of_streams); |
|
641 } |
|
642 |
|
643 virtual void RenderVideoFrame(const unsigned char* buffer, |
|
644 unsigned int buffer_size, |
|
645 uint32_t time_stamp, |
|
646 int64_t render_time, |
|
647 const ImageHandle& handle) { |
|
648 pipeline_->listener_->RenderVideoFrame(buffer, buffer_size, time_stamp, |
|
649 render_time, |
|
650 handle.GetImage()); |
|
651 } |
|
652 |
|
653 private: |
|
654 MediaPipelineReceiveVideo *pipeline_; // Raw pointer to avoid cycles |
|
655 }; |
|
656 |
|
657 // Separate class to allow ref counting |
|
658 class PipelineListener : public GenericReceiveListener { |
|
659 public: |
|
660 PipelineListener(SourceMediaStream * source, TrackID track_id); |
|
661 |
|
662 // Implement MediaStreamListener |
|
663 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, |
|
664 TrackRate rate, |
|
665 TrackTicks offset, |
|
666 uint32_t events, |
|
667 const MediaSegment& queued_media) MOZ_OVERRIDE {} |
|
668 virtual void NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) MOZ_OVERRIDE; |
|
669 |
|
670 // Accessors for external writes from the renderer |
|
671 void FrameSizeChange(unsigned int width, |
|
672 unsigned int height, |
|
673 unsigned int number_of_streams) { |
|
674 ReentrantMonitorAutoEnter enter(monitor_); |
|
675 |
|
676 width_ = width; |
|
677 height_ = height; |
|
678 } |
|
679 |
|
680 void RenderVideoFrame(const unsigned char* buffer, |
|
681 unsigned int buffer_size, |
|
682 uint32_t time_stamp, |
|
683 int64_t render_time, |
|
684 const RefPtr<layers::Image>& video_image); |
|
685 |
|
686 private: |
|
687 int width_; |
|
688 int height_; |
|
689 #ifdef MOZILLA_INTERNAL_API |
|
690 nsRefPtr<layers::ImageContainer> image_container_; |
|
691 nsRefPtr<layers::Image> image_; |
|
692 #endif |
|
693 mozilla::ReentrantMonitor monitor_; // Monitor for processing WebRTC frames. |
|
694 // Protects image_ against: |
|
695 // - Writing from the GIPS thread |
|
696 // - Reading from the MSG thread |
|
697 }; |
|
698 |
|
699 friend class PipelineRenderer; |
|
700 |
|
701 RefPtr<PipelineRenderer> renderer_; |
|
702 RefPtr<PipelineListener> listener_; |
|
703 }; |
|
704 |
|
705 |
|
706 } // end namespace |
|
707 #endif |