Expose codec implementation names in stats.

Used to distinguish between software/hardware encoders/decoders and
other implementation differences. Useful for tracking quality
regressions related to specific implementations.

BUG=webrtc:4897
R=hta@webrtc.org, mflodman@webrtc.org, stefan@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#11084}
diff --git a/talk/app/webrtc/java/jni/androidmediadecoder_jni.cc b/talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
index 49b84aa..7ebcd20 100644
--- a/talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
+++ b/talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
@@ -95,6 +95,8 @@
   // rtc::MessageHandler implementation.
   void OnMessage(rtc::Message* msg) override;
 
+  const char* ImplementationName() const override;
+
  private:
   // CHECK-fail if not running on |codec_thread_|.
   void CheckOnCodecThread();
@@ -906,5 +908,9 @@
   delete decoder;
 }
 
+const char* MediaCodecVideoDecoder::ImplementationName() const {
+  return "MediaCodec";
+}
+
 }  // namespace webrtc_jni
 
diff --git a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
index c6c7594..b36dc0b 100644
--- a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
+++ b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
@@ -109,6 +109,11 @@
   int GetTargetFramerate() override;
 
   bool SupportsNativeHandle() const override { return true; }
+  const char* ImplementationName() const override;
+
+ private:
+  // CHECK-fail if not running on |codec_thread_|.
+  void CheckOnCodecThread();
 
  private:
   // ResetCodecOnCodecThread() calls ReleaseOnCodecThread() and
@@ -1068,8 +1073,12 @@
   return scale_ ? quality_scaler_.GetTargetFramerate() : -1;
 }
 
+const char* MediaCodecVideoEncoder::ImplementationName() const {
+  return "MediaCodec";
+}
+
 MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory()
-  : egl_context_ (nullptr) {
+    : egl_context_(nullptr) {
   JNIEnv* jni = AttachCurrentThreadIfNeeded();
   ScopedLocalRefFrame local_ref_frame(jni);
   jclass j_encoder_class = FindClass(jni, "org/webrtc/MediaCodecVideoEncoder");
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 893e586..b514b42 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -201,6 +201,8 @@
 
 void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
   ExtractCommonReceiveProperties(info, report);
+  report->AddString(StatsReport::kStatsValueNameCodecImplementationName,
+                    info.decoder_implementation_name);
   report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
                    info.bytes_rcvd);
   report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
@@ -233,6 +235,8 @@
 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
   ExtractCommonSendProperties(info, report);
 
+  report->AddString(StatsReport::kStatsValueNameCodecImplementationName,
+                    info.encoder_implementation_name);
   report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
                      (info.adapt_reason & 0x2) > 0);
   report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
diff --git a/talk/app/webrtc/statstypes.cc b/talk/app/webrtc/statstypes.cc
index e45833c..19cb1f5 100644
--- a/talk/app/webrtc/statstypes.cc
+++ b/talk/app/webrtc/statstypes.cc
@@ -408,6 +408,8 @@
       return "state";
     case kStatsValueNameDataChannelId:
       return "datachannelid";
+    case kStatsValueNameCodecImplementationName:
+      return "codecImplementationName";
 
     // 'goog' prefixed constants.
     case kStatsValueNameAccelerateRate:
@@ -592,9 +594,6 @@
       return "googViewLimitedResolution";
     case kStatsValueNameWritable:
       return "googWritable";
-    default:
-      RTC_DCHECK(false);
-      break;
   }
 
   return nullptr;
diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h
index 7fa9f32..60439b9 100644
--- a/talk/app/webrtc/statstypes.h
+++ b/talk/app/webrtc/statstypes.h
@@ -120,6 +120,7 @@
     kStatsValueNameAudioOutputLevel,
     kStatsValueNameBytesReceived,
     kStatsValueNameBytesSent,
+    kStatsValueNameCodecImplementationName,
     kStatsValueNameDataChannelId,
     kStatsValueNamePacketsLost,
     kStatsValueNamePacketsReceived,
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index 0a59019..f6fb77d 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -783,6 +783,7 @@
   }
 
   std::vector<SsrcGroup> ssrc_groups;
+  std::string encoder_implementation_name;
   int packets_cached;
   int firs_rcvd;
   int plis_rcvd;
@@ -828,6 +829,7 @@
   }
 
   std::vector<SsrcGroup> ssrc_groups;
+  std::string decoder_implementation_name;
   int packets_concealed;
   int firs_sent;
   int plis_sent;
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index 5deee96..55c0742 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -2125,6 +2125,7 @@
   if (stats.bw_limited_resolution)
     info.adapt_reason |= CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH;
 
+  info.encoder_implementation_name = stats.encoder_implementation_name;
   info.ssrc_groups = ssrc_groups_;
   info.framerate_input = stats.input_frame_rate;
   info.framerate_sent = stats.encode_frame_rate;
@@ -2517,6 +2518,7 @@
   info.ssrc_groups = ssrc_groups_;
   info.add_ssrc(config_.rtp.remote_ssrc);
   webrtc::VideoReceiveStream::Stats stats = stream_->GetStats();
+  info.decoder_implementation_name = stats.decoder_implementation_name;
   info.bytes_rcvd = stats.rtp_stats.transmitted.payload_bytes +
                     stats.rtp_stats.transmitted.header_bytes +
                     stats.rtp_stats.transmitted.padding_bytes;
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index bd88b9c..6aca234 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -2445,6 +2445,18 @@
   EXPECT_EQ(kVp8Codec.name, info.senders[0].codec_name);
 }
 
+TEST_F(WebRtcVideoChannel2Test, GetStatsReportsEncoderImplementationName) {
+  FakeVideoSendStream* stream = AddSendStream();
+  webrtc::VideoSendStream::Stats stats;
+  stats.encoder_implementation_name = "encoder_implementation_name";
+  stream->SetStats(stats);
+
+  cricket::VideoMediaInfo info;
+  ASSERT_TRUE(channel_->GetStats(&info));
+  EXPECT_EQ(stats.encoder_implementation_name,
+            info.senders[0].encoder_implementation_name);
+}
+
 TEST_F(WebRtcVideoChannel2Test, GetStatsReportsCpuOveruseMetrics) {
   FakeVideoSendStream* stream = AddSendStream();
   webrtc::VideoSendStream::Stats stats;
@@ -2677,6 +2689,7 @@
 TEST_F(WebRtcVideoChannel2Test, GetStatsTranslatesDecodeStatsCorrectly) {
   FakeVideoReceiveStream* stream = AddRecvStream();
   webrtc::VideoReceiveStream::Stats stats;
+  stats.decoder_implementation_name = "decoder_implementation_name";
   stats.decode_ms = 2;
   stats.max_decode_ms = 3;
   stats.current_delay_ms = 4;
@@ -2688,6 +2701,8 @@
 
   cricket::VideoMediaInfo info;
   ASSERT_TRUE(channel_->GetStats(&info));
+  EXPECT_EQ(stats.decoder_implementation_name,
+            info.receivers[0].decoder_implementation_name);
   EXPECT_EQ(stats.decode_ms, info.receivers[0].decode_ms);
   EXPECT_EQ(stats.max_decode_ms, info.receivers[0].max_decode_ms);
   EXPECT_EQ(stats.current_delay_ms, info.receivers[0].current_delay_ms);
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.cc b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.cc
index 6d9a4c1..a0bbb9e 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.cc
@@ -266,6 +266,10 @@
   }
 }
 
+const char* H264VideoToolboxDecoder::ImplementationName() const {
+  return "VideoToolbox";
+}
+
 }  // namespace webrtc
 
 #endif  // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.h b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.h
index f54ddb9..6d64307 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.h
+++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_decoder.h
@@ -45,6 +45,8 @@
 
   int Reset() override;
 
+  const char* ImplementationName() const override;
+
  private:
   int ResetDecompressionSession();
   void ConfigureDecompressionSession();
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
index d677f8b..f47f39c 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
@@ -434,6 +434,10 @@
   }
 }
 
+const char* H264VideoToolboxEncoder::ImplementationName() const {
+  return "VideoToolbox";
+}
+
 }  // namespace webrtc
 
 #endif  // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.h b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.h
index f4fb86f..269e041 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.h
+++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.h
@@ -48,6 +48,8 @@
 
   int Release() override;
 
+  const char* ImplementationName() const override;
+
  private:
   int ResetCompressionSession();
   void ConfigureCompressionSession();
diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
index 605b4d1..a608c10 100644
--- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -301,6 +301,10 @@
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+const char* VP8EncoderImpl::ImplementationName() const {
+  return "libvpx";
+}
+
 void VP8EncoderImpl::SetStreamState(bool send_stream,
                                             int stream_idx) {
   if (send_stream && !send_stream_[stream_idx]) {
@@ -1398,6 +1402,10 @@
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+const char* VP8DecoderImpl::ImplementationName() const {
+  return "libvpx";
+}
+
 int VP8DecoderImpl::CopyReference(VP8DecoderImpl* copy) {
   // The type of frame to copy should be set in ref_frame_->frame_type
   // before the call to this function.
diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h
index 597fb77..e673ad4 100644
--- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h
+++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h
@@ -58,6 +58,8 @@
 
   void OnDroppedFrame() override {}
 
+  const char* ImplementationName() const override;
+
  private:
   void SetupTemporalLayers(int num_streams, int num_temporal_layers,
                            const VideoCodec& codec);
@@ -135,6 +137,8 @@
   int Release() override;
   int Reset() override;
 
+  const char* ImplementationName() const override;
+
  private:
   // Copy reference image from this _decoder to the _decoder in copyTo. Set
   // which frame type to copy in _refFrame->frame_type before the call to
diff --git a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
index 21096bd..a00af64 100644
--- a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -809,6 +809,10 @@
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+const char* VP9EncoderImpl::ImplementationName() const {
+  return "libvpx";
+}
+
 VP9Decoder* VP9Decoder::Create() {
   return new VP9DecoderImpl();
 }
@@ -980,4 +984,9 @@
   inited_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
+
+const char* VP9DecoderImpl::ImplementationName() const {
+  return "libvpx";
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
index 9a48e74..91475c9 100644
--- a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
+++ b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
@@ -47,6 +47,8 @@
 
   void OnDroppedFrame() override {}
 
+  const char* ImplementationName() const override;
+
   struct LayerFrameRefSettings {
     int8_t upd_buf = -1;   // -1 - no update,    0..7 - update buffer 0..7
     int8_t ref_buf1 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
@@ -148,6 +150,8 @@
 
   int Reset() override;
 
+  const char* ImplementationName() const override;
+
  private:
   int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp);
 
diff --git a/webrtc/modules/video_coding/generic_decoder.cc b/webrtc/modules/video_coding/generic_decoder.cc
index dc78ec2..3c72509 100644
--- a/webrtc/modules/video_coding/generic_decoder.cc
+++ b/webrtc/modules/video_coding/generic_decoder.cc
@@ -19,15 +19,12 @@
 
 VCMDecodedFrameCallback::VCMDecodedFrameCallback(VCMTiming& timing,
                                                  Clock* clock)
-:
-_critSect(CriticalSectionWrapper::CreateCriticalSection()),
-_clock(clock),
-_receiveCallback(NULL),
-_timing(timing),
-_timestampMap(kDecoderFrameMemoryLength),
-_lastReceivedPictureID(0)
-{
-}
+    : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
+      _clock(clock),
+      _receiveCallback(NULL),
+      _timing(timing),
+      _timestampMap(kDecoderFrameMemoryLength),
+      _lastReceivedPictureID(0) {}
 
 VCMDecodedFrameCallback::~VCMDecodedFrameCallback()
 {
@@ -115,6 +112,13 @@
     return _lastReceivedPictureID;
 }
 
+void VCMDecodedFrameCallback::OnDecoderImplementationName(
+    const char* implementation_name) {
+  CriticalSectionScoped cs(_critSect);
+  if (_receiveCallback)
+    _receiveCallback->OnDecoderImplementationName(implementation_name);
+}
+
 void VCMDecodedFrameCallback::Map(uint32_t timestamp,
                                   VCMFrameInformation* frameInfo) {
   CriticalSectionScoped cs(_critSect);
@@ -164,6 +168,7 @@
                                    frame.FragmentationHeader(),
                                    frame.CodecSpecific(), frame.RenderTimeMs());
 
+    _callback->OnDecoderImplementationName(_decoder->ImplementationName());
     if (ret < WEBRTC_VIDEO_CODEC_OK)
     {
         LOG(LS_WARNING) << "Failed to decode frame with timestamp "
diff --git a/webrtc/modules/video_coding/generic_decoder.h b/webrtc/modules/video_coding/generic_decoder.h
index 40a9845..b23462f 100644
--- a/webrtc/modules/video_coding/generic_decoder.h
+++ b/webrtc/modules/video_coding/generic_decoder.h
@@ -46,6 +46,7 @@
     virtual int32_t ReceivedDecodedFrame(const uint64_t pictureId);
 
     uint64_t LastReceivedPictureID() const;
+    void OnDecoderImplementationName(const char* implementation_name);
 
     void Map(uint32_t timestamp, VCMFrameInformation* frameInfo);
     int32_t Pop(uint32_t timestamp);
@@ -54,9 +55,9 @@
     // Protect |_receiveCallback| and |_timestampMap|.
     CriticalSectionWrapper* _critSect;
     Clock* _clock;
-    VCMReceiveCallback* _receiveCallback;  // Guarded by |_critSect|.
+    VCMReceiveCallback* _receiveCallback GUARDED_BY(_critSect);
     VCMTiming& _timing;
-    VCMTimestampMap _timestampMap;  // Guarded by |_critSect|.
+    VCMTimestampMap _timestampMap GUARDED_BY(_critSect);
     uint64_t _lastReceivedPictureID;
 };
 
diff --git a/webrtc/modules/video_coding/generic_encoder.cc b/webrtc/modules/video_coding/generic_encoder.cc
index c8180f3..ae5284b 100644
--- a/webrtc/modules/video_coding/generic_encoder.cc
+++ b/webrtc/modules/video_coding/generic_encoder.cc
@@ -150,6 +150,12 @@
   vcm_encoded_frame_callback_->SetRotation(rotation_);
 
   int32_t result = encoder_->Encode(inputFrame, codecSpecificInfo, &frameTypes);
+
+  if (vcm_encoded_frame_callback_) {
+    vcm_encoded_frame_callback_->SignalLastEncoderImplementationUsed(
+        encoder_->ImplementationName());
+  }
+
   if (is_screenshare_ &&
       result == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT) {
     // Target bitrate exceeded, encoder state has been reset - try again.
@@ -224,7 +230,7 @@
   ***************************/
 VCMEncodedFrameCallback::VCMEncodedFrameCallback(
     EncodedImageCallback* post_encode_callback)
-    : _sendCallback(),
+    : send_callback_(),
       _mediaOpt(NULL),
       _payloadType(0),
       _internalSource(false),
@@ -250,27 +256,25 @@
 int32_t
 VCMEncodedFrameCallback::SetTransportCallback(VCMPacketizationCallback* transport)
 {
-    _sendCallback = transport;
+    send_callback_ = transport;
     return VCM_OK;
 }
 
 int32_t VCMEncodedFrameCallback::Encoded(
-    const EncodedImage& encodedImage,
+    const EncodedImage& encoded_image,
     const CodecSpecificInfo* codecSpecificInfo,
     const RTPFragmentationHeader* fragmentationHeader) {
   TRACE_EVENT_INSTANT1("webrtc", "VCMEncodedFrameCallback::Encoded",
-                       "timestamp", encodedImage._timeStamp);
-  RTC_DCHECK(encodedImage._frameType == kVideoFrameKey ||
-             encodedImage._frameType == kVideoFrameDelta);
-  post_encode_callback_->Encoded(encodedImage, NULL, NULL);
+                       "timestamp", encoded_image._timeStamp);
+  post_encode_callback_->Encoded(encoded_image, NULL, NULL);
 
-  if (_sendCallback == NULL) {
+  if (send_callback_ == NULL) {
     return VCM_UNINITIALIZED;
   }
 
 #ifdef DEBUG_ENCODER_BIT_STREAM
   if (_bitStreamAfterEncoder != NULL) {
-    fwrite(encodedImage._buffer, 1, encodedImage._length,
+    fwrite(encoded_image._buffer, 1, encoded_image._length,
            _bitStreamAfterEncoder);
   }
 #endif
@@ -283,25 +287,29 @@
   }
   rtpVideoHeader.rotation = _rotation;
 
-  int32_t callbackReturn = _sendCallback->SendData(
-      _payloadType, encodedImage, *fragmentationHeader, rtpVideoHeaderPtr);
+  int32_t callbackReturn = send_callback_->SendData(
+      _payloadType, encoded_image, *fragmentationHeader, rtpVideoHeaderPtr);
   if (callbackReturn < 0) {
     return callbackReturn;
   }
 
   if (_mediaOpt != NULL) {
-    _mediaOpt->UpdateWithEncodedData(encodedImage);
+    _mediaOpt->UpdateWithEncodedData(encoded_image);
     if (_internalSource)
       return _mediaOpt->DropFrame();  // Signal to encoder to drop next frame.
   }
   return VCM_OK;
 }
 
-void
-VCMEncodedFrameCallback::SetMediaOpt(
-    media_optimization::MediaOptimization *mediaOpt)
-{
-    _mediaOpt = mediaOpt;
+void VCMEncodedFrameCallback::SetMediaOpt(
+    media_optimization::MediaOptimization* mediaOpt) {
+  _mediaOpt = mediaOpt;
+}
+
+void VCMEncodedFrameCallback::SignalLastEncoderImplementationUsed(
+    const char* implementation_name) {
+  if (send_callback_)
+    send_callback_->OnEncoderImplementationName(implementation_name);
 }
 
 }  // namespace webrtc
diff --git a/webrtc/modules/video_coding/generic_encoder.h b/webrtc/modules/video_coding/generic_encoder.h
index 0d8ad50..5346b63 100644
--- a/webrtc/modules/video_coding/generic_encoder.h
+++ b/webrtc/modules/video_coding/generic_encoder.h
@@ -62,9 +62,11 @@
     void SetInternalSource(bool internalSource) { _internalSource = internalSource; };
 
     void SetRotation(VideoRotation rotation) { _rotation = rotation; }
+    void SignalLastEncoderImplementationUsed(
+        const char* encoder_implementation_name);
 
 private:
-    VCMPacketizationCallback* _sendCallback;
+    VCMPacketizationCallback* send_callback_;
     media_optimization::MediaOptimization* _mediaOpt;
     uint8_t _payloadType;
     bool _internalSource;
diff --git a/webrtc/modules/video_coding/include/video_coding_defines.h b/webrtc/modules/video_coding/include/video_coding_defines.h
index 1b72144..d057b55 100644
--- a/webrtc/modules/video_coding/include/video_coding_defines.h
+++ b/webrtc/modules/video_coding/include/video_coding_defines.h
@@ -62,6 +62,8 @@
                            const RTPFragmentationHeader& fragmentationHeader,
                            const RTPVideoHeader* rtpVideoHdr) = 0;
 
+  virtual void OnEncoderImplementationName(const char* implementation_name) {}
+
  protected:
   virtual ~VCMPacketizationCallback() {
   }
@@ -77,6 +79,7 @@
   }
   // Called when the current receive codec changes.
   virtual void OnIncomingPayloadType(int payload_type) {}
+  virtual void OnDecoderImplementationName(const char* implementation_name) {}
 
  protected:
   virtual ~VCMReceiveCallback() {
diff --git a/webrtc/modules/video_coding/video_receiver.cc b/webrtc/modules/video_coding/video_receiver.cc
index f7ac4bc..f074832 100644
--- a/webrtc/modules/video_coding/video_receiver.cc
+++ b/webrtc/modules/video_coding/video_receiver.cc
@@ -287,43 +287,37 @@
   VCMEncodedFrame* frame = _receiver.FrameForDecoding(
       maxWaitTimeMs, nextRenderTimeMs, prefer_late_decoding);
 
-  if (frame == NULL) {
+  if (!frame)
     return VCM_FRAME_NOT_READY;
-  } else {
-    CriticalSectionScoped cs(_receiveCritSect);
 
-    // If this frame was too late, we should adjust the delay accordingly
-    _timing.UpdateCurrentDelay(frame->RenderTimeMs(),
-                               clock_->TimeInMilliseconds());
+  CriticalSectionScoped cs(_receiveCritSect);
 
-    if (pre_decode_image_callback_) {
-      EncodedImage encoded_image(frame->EncodedImage());
-      int qp = -1;
-      if (qp_parser_.GetQp(*frame, &qp)) {
-        encoded_image.qp_ = qp;
-      }
-      pre_decode_image_callback_->Encoded(
-          encoded_image, frame->CodecSpecific(), NULL);
+  // If this frame was too late, we should adjust the delay accordingly
+  _timing.UpdateCurrentDelay(frame->RenderTimeMs(),
+                             clock_->TimeInMilliseconds());
+
+  if (pre_decode_image_callback_) {
+    EncodedImage encoded_image(frame->EncodedImage());
+    int qp = -1;
+    if (qp_parser_.GetQp(*frame, &qp)) {
+      encoded_image.qp_ = qp;
     }
+    pre_decode_image_callback_->Encoded(encoded_image, frame->CodecSpecific(),
+                                        NULL);
+  }
 
 #ifdef DEBUG_DECODER_BIT_STREAM
-    if (_bitStreamBeforeDecoder != NULL) {
-      // Write bit stream to file for debugging purposes
-      if (fwrite(
-              frame->Buffer(), 1, frame->Length(), _bitStreamBeforeDecoder) !=
-          frame->Length()) {
-        return -1;
-      }
-    }
-#endif
-    const int32_t ret = Decode(*frame);
-    _receiver.ReleaseFrame(frame);
-    frame = NULL;
-    if (ret != VCM_OK) {
-      return ret;
+  if (_bitStreamBeforeDecoder != NULL) {
+    // Write bit stream to file for debugging purposes
+    if (fwrite(frame->Buffer(), 1, frame->Length(), _bitStreamBeforeDecoder) !=
+        frame->Length()) {
+      return -1;
     }
   }
-  return VCM_OK;
+#endif
+  const int32_t ret = Decode(*frame);
+  _receiver.ReleaseFrame(frame);
+  return ret;
 }
 
 int32_t VideoReceiver::RequestSliceLossIndication(
diff --git a/webrtc/test/fake_decoder.cc b/webrtc/test/fake_decoder.cc
index 63316e0..dbdd580 100644
--- a/webrtc/test/fake_decoder.cc
+++ b/webrtc/test/fake_decoder.cc
@@ -53,10 +53,16 @@
 int32_t FakeDecoder::Release() {
   return WEBRTC_VIDEO_CODEC_OK;
 }
+
 int32_t FakeDecoder::Reset() {
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+const char* FakeDecoder::kImplementationName = "fake_decoder";
+const char* FakeDecoder::ImplementationName() const {
+  return kImplementationName;
+}
+
 int32_t FakeH264Decoder::Decode(const EncodedImage& input,
                                 bool missing_frames,
                                 const RTPFragmentationHeader* fragmentation,
diff --git a/webrtc/test/fake_decoder.h b/webrtc/test/fake_decoder.h
index 20f6bbf..0da961d 100644
--- a/webrtc/test/fake_decoder.h
+++ b/webrtc/test/fake_decoder.h
@@ -39,6 +39,10 @@
   int32_t Release() override;
   int32_t Reset() override;
 
+  const char* ImplementationName() const override;
+
+  static const char* kImplementationName;
+
  private:
   VideoCodec config_;
   VideoFrame frame_;
diff --git a/webrtc/test/fake_encoder.cc b/webrtc/test/fake_encoder.cc
index 165fd3e..72df40f 100644
--- a/webrtc/test/fake_encoder.cc
+++ b/webrtc/test/fake_encoder.cc
@@ -132,6 +132,11 @@
   return 0;
 }
 
+const char* FakeEncoder::kImplementationName = "fake_encoder";
+const char* FakeEncoder::ImplementationName() const {
+  return kImplementationName;
+}
+
 FakeH264Encoder::FakeH264Encoder(Clock* clock)
     : FakeEncoder(clock), callback_(NULL), idr_counter_(0) {
   FakeEncoder::RegisterEncodeCompleteCallback(this);
diff --git a/webrtc/test/fake_encoder.h b/webrtc/test/fake_encoder.h
index 88ff9d4..6bff00e 100644
--- a/webrtc/test/fake_encoder.h
+++ b/webrtc/test/fake_encoder.h
@@ -39,6 +39,9 @@
   int32_t Release() override;
   int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
   int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) override;
+  const char* ImplementationName() const override;
+
+  static const char* kImplementationName;
 
  protected:
   Clock* const clock_;
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index 46741b0..bd9c198 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -2383,6 +2383,9 @@
         receive_stats_filled_["IncomingRate"] |=
             stats.network_frame_rate != 0 || stats.total_bitrate_bps != 0;
 
+        send_stats_filled_["DecoderImplementationName"] |=
+            stats.decoder_implementation_name ==
+            test::FakeDecoder::kImplementationName;
         receive_stats_filled_["RenderDelayAsHighAsExpected"] |=
             stats.render_delay_ms >= kExpectedRenderDelayMs;
 
@@ -2438,6 +2441,10 @@
       send_stats_filled_["CpuOveruseMetrics"] |=
           stats.avg_encode_time_ms != 0 || stats.encode_usage_percent != 0;
 
+      send_stats_filled_["EncoderImplementationName"] |=
+          stats.encoder_implementation_name ==
+          test::FakeEncoder::kImplementationName;
+
       for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
                stats.substreams.begin();
            it != stats.substreams.end(); ++it) {
diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
index 329905d..c13c807 100644
--- a/webrtc/video/receive_statistics_proxy.cc
+++ b/webrtc/video/receive_statistics_proxy.cc
@@ -80,6 +80,11 @@
   stats_.current_payload_type = payload_type;
 }
 
+void ReceiveStatisticsProxy::OnDecoderImplementationName(
+    const char* implementation_name) {
+  rtc::CritScope lock(&crit_);
+  stats_.decoder_implementation_name = implementation_name;
+}
 void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate,
                                             unsigned int bitrate_bps) {
   rtc::CritScope lock(&crit_);
diff --git a/webrtc/video/receive_statistics_proxy.h b/webrtc/video/receive_statistics_proxy.h
index 91f4105..87cb950 100644
--- a/webrtc/video/receive_statistics_proxy.h
+++ b/webrtc/video/receive_statistics_proxy.h
@@ -45,6 +45,7 @@
   void OnDecodedFrame();
   void OnRenderedFrame(int width, int height);
   void OnIncomingPayloadType(int payload_type);
+  void OnDecoderImplementationName(const char* implementation_name);
   void OnIncomingRate(unsigned int framerate, unsigned int bitrate_bps);
   void OnDecoderTiming(int decode_ms,
                        int max_decode_ms,
diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc
index f5e7e3d..c198ad2 100644
--- a/webrtc/video/send_statistics_proxy.cc
+++ b/webrtc/video/send_statistics_proxy.cc
@@ -161,6 +161,12 @@
   }
 }
 
+void SendStatisticsProxy::OnEncoderImplementationName(
+    const char* implementation_name) {
+  rtc::CritScope lock(&crit_);
+  stats_.encoder_implementation_name = implementation_name;
+}
+
 void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) {
   rtc::CritScope lock(&crit_);
   stats_.encode_frame_rate = framerate;
diff --git a/webrtc/video/send_statistics_proxy.h b/webrtc/video/send_statistics_proxy.h
index e9e89f6..7f6df06 100644
--- a/webrtc/video/send_statistics_proxy.h
+++ b/webrtc/video/send_statistics_proxy.h
@@ -58,6 +58,7 @@
   // From VideoEncoderRateObserver.
   void OnSetRates(uint32_t bitrate_bps, int framerate) override;
 
+  void OnEncoderImplementationName(const char* implementation_name);
   void OnOutgoingRate(uint32_t framerate, uint32_t bitrate);
   void OnSuspendChange(bool is_suspended);
   void OnInactiveSsrc(uint32_t ssrc);
diff --git a/webrtc/video/video_decoder.cc b/webrtc/video/video_decoder.cc
index 2a0151d..d699175 100644
--- a/webrtc/video/video_decoder.cc
+++ b/webrtc/video/video_decoder.cc
@@ -76,6 +76,9 @@
   }
   if (callback_ != nullptr)
     fallback_decoder_->RegisterDecodeCompleteCallback(callback_);
+  fallback_implementation_name_ =
+      std::string(fallback_decoder_->ImplementationName()) +
+      " (fallback from: " + decoder_->ImplementationName() + ")";
   return true;
 }
 
@@ -137,4 +140,10 @@
   return decoder_->PrefersLateDecoding();
 }
 
+const char* VideoDecoderSoftwareFallbackWrapper::ImplementationName() const {
+  if (fallback_decoder_)
+    return fallback_implementation_name_.c_str();
+  return decoder_->ImplementationName();
+}
+
 }  // namespace webrtc
diff --git a/webrtc/video/video_decoder_unittest.cc b/webrtc/video/video_decoder_unittest.cc
index 422aca0..4d54a3e 100644
--- a/webrtc/video/video_decoder_unittest.cc
+++ b/webrtc/video/video_decoder_unittest.cc
@@ -53,6 +53,11 @@
       ++reset_count_;
       return WEBRTC_VIDEO_CODEC_OK;
     }
+
+    const char* ImplementationName() const override {
+      return "fake-decoder";
+    }
+
     int init_decode_count_ = 0;
     int decode_count_ = 0;
     int32_t decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
@@ -144,7 +149,7 @@
 }
 
 // TODO(pbos): Fake a VP8 frame well enough to actually receive a callback from
-// the software encoder.
+// the software decoder.
 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
        ForwardsRegisterDecodeCompleteCallback) {
   class FakeDecodedImageCallback : public DecodedImageCallback {
@@ -168,4 +173,19 @@
   EXPECT_EQ(&callback2, fake_decoder_.decode_complete_callback_);
 }
 
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+       ReportsFallbackImplementationName) {
+  VideoCodec codec = {};
+  fallback_wrapper_.InitDecode(&codec, 2);
+
+  fake_decoder_.decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+  EncodedImage encoded_image;
+  fallback_wrapper_.Decode(encoded_image, false, nullptr, nullptr, -1);
+  // Hard coded expected value since libvpx is the software implementation name
+  // for VP8. Change accordingly if the underlying implementation does.
+  EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
+               fallback_wrapper_.ImplementationName());
+  fallback_wrapper_.Release();
+}
+
 }  // namespace webrtc
diff --git a/webrtc/video/video_encoder.cc b/webrtc/video/video_encoder.cc
index 6410e39..e85e3d97 100644
--- a/webrtc/video/video_encoder.cc
+++ b/webrtc/video/video_encoder.cc
@@ -76,6 +76,9 @@
   if (channel_parameters_set_)
     fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
 
+  fallback_implementation_name_ =
+      std::string(fallback_encoder_->ImplementationName()) +
+      " (fallback from: " + encoder_->ImplementationName() + ")";
   // Since we're switching to the fallback encoder, Release the real encoder. It
   // may be re-initialized via InitEncode later, and it will continue to get
   // Set calls for rates and channel parameters in the meantime.
@@ -182,6 +185,12 @@
   return encoder_->SupportsNativeHandle();
 }
 
+const char* VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
+  if (fallback_encoder_)
+    return fallback_implementation_name_.c_str();
+  return encoder_->ImplementationName();
+}
+
 int VideoEncoderSoftwareFallbackWrapper::GetTargetFramerate() {
   if (fallback_encoder_)
     return fallback_encoder_->GetTargetFramerate();
diff --git a/webrtc/video/video_encoder_unittest.cc b/webrtc/video/video_encoder_unittest.cc
index b800a74..0f28f89 100644
--- a/webrtc/video/video_encoder_unittest.cc
+++ b/webrtc/video/video_encoder_unittest.cc
@@ -67,6 +67,10 @@
       return false;
     }
 
+    const char* ImplementationName() const override {
+      return "fake-encoder";
+    }
+
     int init_encode_count_ = 0;
     int32_t init_encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
     int32_t encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
@@ -259,4 +263,13 @@
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.Release());
 }
 
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+       ReportsFallbackImplementationName) {
+  UtilizeFallbackEncoder();
+  // Hard coded expected value since libvpx is the software implementation name
+  // for VP8. Change accordingly if the underlying implementation does.
+  EXPECT_STREQ("libvpx (fallback from: fake-encoder)",
+               fallback_wrapper_.ImplementationName());
+}
+
 }  // namespace webrtc
diff --git a/webrtc/video/vie_channel.cc b/webrtc/video/vie_channel.cc
index 6e2406a..e39ffbf 100644
--- a/webrtc/video/vie_channel.cc
+++ b/webrtc/video/vie_channel.cc
@@ -983,6 +983,12 @@
     receive_stats_callback_->OnIncomingPayloadType(payload_type);
 }
 
+void ViEChannel::OnDecoderImplementationName(const char* implementation_name) {
+  CriticalSectionScoped cs(crit_.get());
+  if (receive_stats_callback_)
+    receive_stats_callback_->OnDecoderImplementationName(implementation_name);
+}
+
 void ViEChannel::OnReceiveRatesUpdated(uint32_t bit_rate, uint32_t frame_rate) {
   CriticalSectionScoped cs(crit_.get());
   if (receive_stats_callback_)
diff --git a/webrtc/video/vie_channel.h b/webrtc/video/vie_channel.h
index 8f99260..25d06b9 100644
--- a/webrtc/video/vie_channel.h
+++ b/webrtc/video/vie_channel.h
@@ -230,6 +230,7 @@
 
   // Implements VCMReceiveCallback.
   void OnIncomingPayloadType(int payload_type) override;
+  void OnDecoderImplementationName(const char* implementation_name) override;
 
   // Implements VCMReceiveStatisticsCallback.
   void OnReceiveRatesUpdated(uint32_t bit_rate, uint32_t frame_rate) override;
diff --git a/webrtc/video/vie_encoder.cc b/webrtc/video/vie_encoder.cc
index 803825c..a147b24 100644
--- a/webrtc/video/vie_encoder.cc
+++ b/webrtc/video/vie_encoder.cc
@@ -472,6 +472,12 @@
              : -1;
 }
 
+void ViEEncoder::OnEncoderImplementationName(
+    const char* implementation_name) {
+  if (stats_proxy_)
+    stats_proxy_->OnEncoderImplementationName(implementation_name);
+}
+
 int32_t ViEEncoder::SendStatistics(const uint32_t bit_rate,
                                    const uint32_t frame_rate) {
   if (stats_proxy_)
diff --git a/webrtc/video/vie_encoder.h b/webrtc/video/vie_encoder.h
index b86287c..a15fd89 100644
--- a/webrtc/video/vie_encoder.h
+++ b/webrtc/video/vie_encoder.h
@@ -112,6 +112,7 @@
                    const EncodedImage& encoded_image,
                    const RTPFragmentationHeader& fragmentation_header,
                    const RTPVideoHeader* rtp_video_hdr) override;
+  void OnEncoderImplementationName(const char* implementation_name) override;
 
   // Implements VideoSendStatisticsCallback.
   int32_t SendStatistics(const uint32_t bit_rate,
diff --git a/webrtc/video_decoder.h b/webrtc/video_decoder.h
index 30e2779..3cd94e8 100644
--- a/webrtc/video_decoder.h
+++ b/webrtc/video_decoder.h
@@ -11,6 +11,7 @@
 #ifndef WEBRTC_VIDEO_DECODER_H_
 #define WEBRTC_VIDEO_DECODER_H_
 
+#include <string>
 #include <vector>
 
 #include "webrtc/common_types.h"
@@ -78,6 +79,8 @@
   // That is, it can not decode infinite number of frames before the decoded
   // frame is consumed.
   virtual bool PrefersLateDecoding() const { return true; }
+
+  virtual const char* ImplementationName() const { return "unknown"; }
 };
 
 // Class used to wrap external VideoDecoders to provide a fallback option on
@@ -104,6 +107,8 @@
   int32_t Reset() override;
   bool PrefersLateDecoding() const override;
 
+  const char* ImplementationName() const override;
+
  private:
   bool InitFallbackDecoder();
 
@@ -112,6 +117,7 @@
 
   VideoCodec codec_settings_;
   int32_t number_of_cores_;
+  std::string fallback_implementation_name_;
   rtc::scoped_ptr<VideoDecoder> fallback_decoder_;
   DecodedImageCallback* callback_;
 };
diff --git a/webrtc/video_encoder.h b/webrtc/video_encoder.h
index f255336..9e7e4d7 100644
--- a/webrtc/video_encoder.h
+++ b/webrtc/video_encoder.h
@@ -11,6 +11,7 @@
 #ifndef WEBRTC_VIDEO_ENCODER_H_
 #define WEBRTC_VIDEO_ENCODER_H_
 
+#include <string>
 #include <vector>
 
 #include "webrtc/common_types.h"
@@ -124,6 +125,7 @@
   virtual void OnDroppedFrame() {}
   virtual int GetTargetFramerate() { return -1; }
   virtual bool SupportsNativeHandle() const { return false; }
+  virtual const char* ImplementationName() const { return "unknown"; }
 };
 
 // Class used to wrap external VideoEncoders to provide a fallback option on
@@ -151,6 +153,7 @@
   void OnDroppedFrame() override;
   int GetTargetFramerate() override;
   bool SupportsNativeHandle() const override;
+  const char* ImplementationName() const override;
 
  private:
   bool InitFallbackEncoder();
@@ -175,6 +178,7 @@
   webrtc::VideoEncoder* const encoder_;
 
   rtc::scoped_ptr<webrtc::VideoEncoder> fallback_encoder_;
+  std::string fallback_implementation_name_;
   EncodedImageCallback* callback_;
 };
 }  // namespace webrtc
diff --git a/webrtc/video_receive_stream.h b/webrtc/video_receive_stream.h
index cd1434c..51d8426 100644
--- a/webrtc/video_receive_stream.h
+++ b/webrtc/video_receive_stream.h
@@ -51,6 +51,7 @@
     int render_frame_rate = 0;
 
     // Decoder stats.
+    std::string decoder_implementation_name = "unknown";
     FrameCounts frame_counts;
     int decode_ms = 0;
     int max_decode_ms = 0;
diff --git a/webrtc/video_send_stream.h b/webrtc/video_send_stream.h
index b5f8c9a..83a96d3 100644
--- a/webrtc/video_send_stream.h
+++ b/webrtc/video_send_stream.h
@@ -62,6 +62,7 @@
   };
 
   struct Stats {
+    std::string encoder_implementation_name = "unknown";
     int input_frame_rate = 0;
     int encode_frame_rate = 0;
     int avg_encode_time_ms = 0;