Remove send and receive streams when destroyed.
Fixes crash where packets were sent to a receive stream that had been
destroyed but not removed from the ssrc mapping from call to receiver.
Added a repro case that reliably crashed before the fix.
BUG=
R=stefan@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/2161007
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4681 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/internal/video_call.cc b/webrtc/video_engine/internal/video_call.cc
index 97f606a..6e50395 100644
--- a/webrtc/video_engine/internal/video_call.cc
+++ b/webrtc/video_engine/internal/video_call.cc
@@ -95,11 +95,25 @@
SendStreamState* VideoCall::DestroySendStream(
webrtc::VideoSendStream* send_stream) {
- if (send_stream == NULL) {
- return NULL;
+ assert(send_stream != NULL);
+
+ VideoSendStream* send_stream_impl = NULL;
+ {
+ WriteLockScoped write_lock(*send_lock_);
+ for (std::map<uint32_t, VideoSendStream*>::iterator it =
+ send_ssrcs_.begin();
+ it != send_ssrcs_.end();
+ ++it) {
+ if (it->second == static_cast<VideoSendStream*>(send_stream)) {
+ send_stream_impl = it->second;
+ send_ssrcs_.erase(it);
+ break;
+ }
+ }
}
- // TODO(pbos): Remove it properly! Free the SSRCs!
- delete static_cast<VideoSendStream*>(send_stream);
+
+ assert(send_stream_impl != NULL);
+ delete send_stream_impl;
// TODO(pbos): Return its previous state
return NULL;
@@ -122,11 +136,25 @@
void VideoCall::DestroyReceiveStream(
webrtc::VideoReceiveStream* receive_stream) {
- if (receive_stream == NULL) {
- return;
+ assert(receive_stream != NULL);
+
+ VideoReceiveStream* receive_stream_impl = NULL;
+ {
+ WriteLockScoped write_lock(*receive_lock_);
+ for (std::map<uint32_t, VideoReceiveStream*>::iterator it =
+ receive_ssrcs_.begin();
+ it != receive_ssrcs_.end();
+ ++it) {
+ if (it->second == static_cast<VideoReceiveStream*>(receive_stream)) {
+ receive_stream_impl = it->second;
+ receive_ssrcs_.erase(it);
+ break;
+ }
+ }
}
- // TODO(pbos): Remove its SSRCs!
- delete static_cast<VideoReceiveStream*>(receive_stream);
+
+ assert(receive_stream_impl != NULL);
+ delete receive_stream_impl;
}
uint32_t VideoCall::SendBitrateEstimate() {
diff --git a/webrtc/video_engine/test/engine_tests.cc b/webrtc/video_engine/test/engine_tests.cc
index 61a0f5c..7a449af 100644
--- a/webrtc/video_engine/test/engine_tests.cc
+++ b/webrtc/video_engine/test/engine_tests.cc
@@ -256,13 +256,17 @@
void StopSending() {
frame_generator_capturer_->Stop();
- send_stream_->StopSend();
- receive_stream_->StopReceive();
+ if (send_stream_ != NULL)
+ send_stream_->StopSend();
+ if (receive_stream_ != NULL)
+ receive_stream_->StopReceive();
}
void DestroyStreams() {
- sender_call_->DestroySendStream(send_stream_);
- receiver_call_->DestroyReceiveStream(receive_stream_);
+ if (send_stream_ != NULL)
+ sender_call_->DestroySendStream(send_stream_);
+ if (receive_stream_ != NULL)
+ receiver_call_->DestroyReceiveStream(receive_stream_);
send_stream_= NULL;
receive_stream_ = NULL;
}
@@ -553,5 +557,59 @@
ReceivesPliAndRecovers(0);
}
+TEST_P(EngineTest, SurvivesIncomingRtpPacketsToDestroyedReceiveStream) {
+ class PacketInputObserver : public PacketReceiver {
+ public:
+ explicit PacketInputObserver(PacketReceiver* receiver)
+ : receiver_(receiver), delivered_packet_(EventWrapper::Create()) {}
+
+ EventTypeWrapper Wait() {
+ return delivered_packet_->Wait(30 * 1000);
+ }
+
+ private:
+ virtual bool DeliverPacket(const uint8_t* packet, size_t length) {
+ if (RtpHeaderParser::IsRtcp(packet, static_cast<int>(length))) {
+ return receiver_->DeliverPacket(packet, length);
+ } else {
+ EXPECT_FALSE(receiver_->DeliverPacket(packet, length));
+ delivered_packet_->Set();
+ return false;
+ }
+ }
+
+ PacketReceiver* receiver_;
+ scoped_ptr<EventWrapper> delivered_packet_;
+ };
+
+ test::DirectTransport send_transport, receive_transport;
+
+ CreateCalls(&send_transport, &receive_transport);
+ PacketInputObserver input_observer(receiver_call_->Receiver());
+
+ send_transport.SetReceiver(&input_observer);
+ receive_transport.SetReceiver(sender_call_->Receiver());
+
+ CreateTestConfigs();
+
+ CreateStreams();
+ CreateFrameGenerator();
+
+ StartSending();
+
+ receiver_call_->DestroyReceiveStream(receive_stream_);
+ receive_stream_ = NULL;
+
+ // Wait() waits for a received packet.
+ EXPECT_EQ(kEventSignaled, input_observer.Wait());
+
+ StopSending();
+
+ DestroyStreams();
+
+ send_transport.StopSending();
+ receive_transport.StopSending();
+}
+
INSTANTIATE_TEST_CASE_P(EngineTest, EngineTest, ::testing::Values(video_vga));
} // namespace webrtc