Adding "first packet received" notification to PeerConnectionObserver.

R=glaznev@webrtc.org, pthatcher@webrtc.org, tkchin@webrtc.org

Review URL: https://codereview.webrtc.org/1581693006 .

Cr-Commit-Position: refs/heads/master@{#11401}
diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc
index 1e138f6..85e10a9 100644
--- a/talk/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc
@@ -374,6 +374,14 @@
     CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
   }
 
+  void OnFirstMediaPacketReceived() override {
+    ScopedLocalRefFrame local_ref_frame(jni());
+    jmethodID m = GetMethodID(jni(), *j_observer_class_,
+                              "onFirstMediaPacketReceived", "()V");
+    jni()->CallVoidMethod(*j_observer_global_, m);
+    CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
+  }
+
   void SetConstraints(ConstraintsWrapper* constraints) {
     RTC_CHECK(!constraints_.get()) << "constraints already set!";
     constraints_.reset(constraints);
diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
index 36cd075..331c062 100644
--- a/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
+++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnection.java
@@ -86,6 +86,9 @@
 
     /** Triggered when renegotiation is necessary. */
     public void onRenegotiationNeeded();
+
+    /** Called when the first RTP packet is received. */
+    public void onFirstMediaPacketReceived();
   }
 
   /** Java version of PeerConnectionInterface.IceServer. */
diff --git a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java
index 50f4d73..8db0ded 100644
--- a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java
+++ b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java
@@ -289,6 +289,9 @@
       ++expectedStatsCallbacks;
     }
 
+    @Override
+    public synchronized void onFirstMediaPacketReceived() {}
+
     public synchronized LinkedList<StatsReport[]> takeStatsReports() {
       LinkedList<StatsReport[]> got = gotStatsReports;
       gotStatsReports = new LinkedList<StatsReport[]>();
diff --git a/talk/app/webrtc/objc/RTCPeerConnectionObserver.h b/talk/app/webrtc/objc/RTCPeerConnectionObserver.h
index 9b981b9..f5fb8dc 100644
--- a/talk/app/webrtc/objc/RTCPeerConnectionObserver.h
+++ b/talk/app/webrtc/objc/RTCPeerConnectionObserver.h
@@ -68,6 +68,9 @@
   // New Ice candidate have been found.
   void OnIceCandidate(const IceCandidateInterface* candidate) override;
 
+  // Called when the first RTP packet is received.
+  void OnFirstMediaPacketReceived() override;
+
  private:
   __weak RTCPeerConnection* _peerConnection;
 };
diff --git a/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm b/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm
index 411cd6c..47246cb 100644
--- a/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm
+++ b/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm
@@ -105,4 +105,9 @@
                            gotICECandidate:iceCandidate];
 }
 
+void RTCPeerConnectionObserver::OnFirstMediaPacketReceived() {
+  id<RTCPeerConnectionDelegate> delegate = _peerConnection.delegate;
+  [delegate peerConnectionOnFirstMediaPacketReceived:_peerConnection];
+}
+
 }  // namespace webrtc
diff --git a/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h b/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h
index bf0c231..63529cb 100644
--- a/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h
+++ b/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h
@@ -69,4 +69,8 @@
 - (void)peerConnection:(RTCPeerConnection*)peerConnection
     didOpenDataChannel:(RTCDataChannel*)dataChannel;
 
+// Called when the first RTP packet is received.
+- (void)peerConnectionOnFirstMediaPacketReceived:
+    (RTCPeerConnection *)peerConnection;
+
 @end
diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
index 892c461..3f23726 100644
--- a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
+++ b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
@@ -233,6 +233,10 @@
            @"Unexpected state");
 }
 
+- (void)peerConnectionOnFirstMediaPacketReceived:
+    (RTCPeerConnection*)peerConnection {
+}
+
 #pragma mark - RTCDataChannelDelegate
 
 - (void)channelDidChangeState:(RTCDataChannel*)channel {
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index beae770..d2f8d77 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -663,6 +663,8 @@
       this, &PeerConnection::OnDataChannelDestroyed);
   session_->SignalDataChannelOpenMessage.connect(
       this, &PeerConnection::OnDataChannelOpenMessage);
+  session_->SignalFirstMediaPacketReceived.connect(
+      this, &PeerConnection::OnFirstMediaPacketReceived);
   return true;
 }
 
@@ -2029,6 +2031,11 @@
       DataChannelProxy::Create(signaling_thread(), channel));
 }
 
+void PeerConnection::OnFirstMediaPacketReceived() {
+  RTC_DCHECK(signaling_thread()->IsCurrent());
+  observer_->OnFirstMediaPacketReceived();
+}
+
 RtpSenderInterface* PeerConnection::FindSenderById(const std::string& id) {
   auto it =
       std::find_if(senders_.begin(), senders_.end(),
diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h
index 03b5853..83c408b 100644
--- a/talk/app/webrtc/peerconnection.h
+++ b/talk/app/webrtc/peerconnection.h
@@ -326,6 +326,7 @@
   // webrtc::DataChannel should be opened.
   void OnDataChannelOpenMessage(const std::string& label,
                                 const InternalDataChannelInit& config);
+  void OnFirstMediaPacketReceived();
 
   RtpSenderInterface* FindSenderById(const std::string& id);
 
diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc
index cf53c44..9e5ca2f 100644
--- a/talk/app/webrtc/peerconnection_unittest.cc
+++ b/talk/app/webrtc/peerconnection_unittest.cc
@@ -251,6 +251,7 @@
     signaling_message_receiver_->ReceiveIceMessage(
         candidate->sdp_mid(), candidate->sdp_mline_index(), ice_sdp);
   }
+  void OnFirstMediaPacketReceived() override { received_media_packet_ = true; }
 
   // MediaStreamInterface callback
   void OnChanged() override {
@@ -386,6 +387,8 @@
     return true;
   }
 
+  bool received_media_packet() const { return received_media_packet_; }
+
   void OnIceComplete() override { LOG(INFO) << id_ << "OnIceComplete"; }
 
   void OnDataChannel(DataChannelInterface* data_channel) override {
@@ -924,6 +927,10 @@
 
   rtc::scoped_refptr<DataChannelInterface> data_channel_;
   rtc::scoped_ptr<MockDataChannelObserver> data_observer_;
+
+  // "true" if the PeerConnection indicated that a packet was received,
+  // through PeerConnectionObserverInterface.
+  bool received_media_packet_ = false;
 };
 
 class P2PTestConductor : public testing::Test {
@@ -1104,6 +1111,10 @@
 
     EXPECT_TRUE_WAIT(FramesNotPending(audio_frame_count, video_frame_count),
                      kMaxWaitForFramesMs);
+    if (audio_frame_count != -1 || video_frame_count != -1) {
+      EXPECT_TRUE(initiating_client_->received_media_packet());
+      EXPECT_TRUE(receiving_client_->received_media_packet());
+    }
   }
 
   void SetupAndVerifyDtlsCall() {
diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h
index e449dc4..42b5ae0 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -501,6 +501,9 @@
   // Called when the ICE connection receiving status changes.
   virtual void OnIceConnectionReceivingChange(bool receiving) {}
 
+  // Called when the first RTP packet is received.
+  virtual void OnFirstMediaPacketReceived() {}
+
  protected:
   // Dtor protected as objects shouldn't be deleted via this interface.
   ~PeerConnectionObserver() {}
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index c6d1856..0270c55 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -1844,6 +1844,8 @@
 
   voice_channel_->SignalDtlsSetupFailure.connect(
       this, &WebRtcSession::OnDtlsSetupFailure);
+  voice_channel_->SignalFirstPacketReceived.connect(
+      this, &WebRtcSession::OnChannelFirstPacketReceived);
 
   SignalVoiceChannelCreated();
   voice_channel_->transport_channel()->SignalSentPacket.connect(
@@ -1861,6 +1863,8 @@
 
   video_channel_->SignalDtlsSetupFailure.connect(
       this, &WebRtcSession::OnDtlsSetupFailure);
+  video_channel_->SignalFirstPacketReceived.connect(
+      this, &WebRtcSession::OnChannelFirstPacketReceived);
 
   SignalVideoChannelCreated();
   video_channel_->transport_channel()->SignalSentPacket.connect(
@@ -1895,6 +1899,14 @@
            rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp);
 }
 
+void WebRtcSession::OnChannelFirstPacketReceived(cricket::BaseChannel*) {
+  ASSERT(signaling_thread()->IsCurrent());
+  if (!has_received_media_packet_) {
+    has_received_media_packet_ = true;
+    SignalFirstMediaPacketReceived();
+  }
+}
+
 void WebRtcSession::OnDataChannelMessageReceived(
     cricket::DataChannel* channel,
     const cricket::ReceiveDataParams& params,
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index cd3f896..3848014 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -309,6 +309,7 @@
   void OnCertificateReady(
       const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
   void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp);
+  void OnChannelFirstPacketReceived(cricket::BaseChannel*);
 
   // For unit test.
   bool waiting_for_certificate_for_testing() const;
@@ -335,6 +336,9 @@
   sigslot::signal2<const std::string&, const InternalDataChannelInit&>
       SignalDataChannelOpenMessage;
 
+  // Called when the first RTP packet is received.
+  sigslot::signal0<> SignalFirstMediaPacketReceived;
+
  private:
   // Indicates the type of SessionDescription in a call to SetLocalDescription
   // and SetRemoteDescription.
@@ -518,6 +522,8 @@
   // Declares the RTCP mux policy for the WebRTCSession.
   PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy_;
 
+  bool has_received_media_packet_ = false;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession);
 };
 }  // namespace webrtc
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index eb4d959..91f46b2 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -1000,6 +1000,10 @@
       // No need to do anything; AppRTC follows a pre-agreed-upon
       // signaling/negotiation protocol.
     }
+
+    @Override
+    public void onFirstMediaPacketReceived() {
+    }
   }
 
   // Implementation detail: handle offer creation/signaling and answer setting,
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
index 33e00ed..50fac5d 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -384,6 +384,11 @@
     didOpenDataChannel:(RTCDataChannel *)dataChannel {
 }
 
+- (void)peerConnectionOnFirstMediaPacketReceived:
+    (RTCPeerConnection *)peerConnection {
+  RTCLog(@"Received first media packet.");
+}
+
 #pragma mark - RTCStatsDelegate
 
 - (void)peerConnection:(RTCPeerConnection *)peerConnection