davidliu
Committed by GitHub

Fix race condition with videoTrack.addRenderer (#448)

@@ -52,6 +52,16 @@ abstract class Track( @@ -52,6 +52,16 @@ abstract class Track(
52 var statsGetter: RTCStatsGetter? = null 52 var statsGetter: RTCStatsGetter? = null
53 53
54 /** 54 /**
  55 + * [MediaStreamTrack] doesn't expose a way to tell if it's disposed. Track it here.
  56 + * This can only be safely accessed on the rtc thread.
  57 + *
  58 + * Note: [MediaStreamTrack] can still be disposed if we don't own it, so this isn't necessarily safe,
  59 + * however generally all the tracks passed into this class are owned by this class.
  60 + */
  61 + internal var isDisposed = false
  62 + private set
  63 +
  64 + /**
55 * Return the [RTCStatsReport] for this track, or null if none is available. 65 * Return the [RTCStatsReport] for this track, or null if none is available.
56 */ 66 */
57 suspend fun getRTCStats(): RTCStatsReport? = statsGetter?.getStats() 67 suspend fun getRTCStats(): RTCStatsReport? = statsGetter?.getStats()
@@ -172,6 +182,7 @@ abstract class Track( @@ -172,6 +182,7 @@ abstract class Track(
172 */ 182 */
173 open fun dispose() { 183 open fun dispose() {
174 executeBlockingOnRTCThread { 184 executeBlockingOnRTCThread {
  185 + isDisposed = true
175 rtcTrack.dispose() 186 rtcTrack.dispose()
176 } 187 }
177 } 188 }
@@ -35,8 +35,10 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : @@ -35,8 +35,10 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
35 */ 35 */
36 open fun addRenderer(renderer: VideoSink) { 36 open fun addRenderer(renderer: VideoSink) {
37 executeBlockingOnRTCThread { 37 executeBlockingOnRTCThread {
38 - sinks.add(renderer)  
39 - rtcTrack.addSink(renderer) 38 + if (!isDisposed) {
  39 + sinks.add(renderer)
  40 + rtcTrack.addSink(renderer)
  41 + }
40 } 42 }
41 } 43 }
42 44
@@ -45,15 +47,19 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : @@ -45,15 +47,19 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
45 */ 47 */
46 open fun removeRenderer(renderer: VideoSink) { 48 open fun removeRenderer(renderer: VideoSink) {
47 executeBlockingOnRTCThread { 49 executeBlockingOnRTCThread {
48 - rtcTrack.removeSink(renderer)  
49 - sinks.remove(renderer) 50 + if (!isDisposed) {
  51 + rtcTrack.removeSink(renderer)
  52 + sinks.remove(renderer)
  53 + }
50 } 54 }
51 } 55 }
52 56
53 override fun stop() { 57 override fun stop() {
54 executeBlockingOnRTCThread { 58 executeBlockingOnRTCThread {
55 - for (sink in sinks) {  
56 - rtcTrack.removeSink(sink) 59 + if (!isDisposed) {
  60 + for (sink in sinks) {
  61 + rtcTrack.removeSink(sink)
  62 + }
57 } 63 }
58 sinks.clear() 64 sinks.clear()
59 } 65 }