Implement and test EncodedImageCallback in new ViE API.

R=mflodman@webrtc.org, pbos@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/4059004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@5179 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/call_tests.cc b/call_tests.cc
index 773886d..5393286 100644
--- a/call_tests.cc
+++ b/call_tests.cc
@@ -18,6 +18,7 @@
 
 #include "webrtc/call.h"
 #include "webrtc/common_video/test/frame_generator.h"
+#include "webrtc/frame_callback.h"
 #include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
@@ -1040,4 +1041,77 @@
   VoiceEngine::Delete(voice_engine);
 }
 
+TEST_F(CallTest, ObserversEncodedFrames) {
+  class EncodedFrameTestObserver : public EncodedFrameObserver {
+   public:
+    EncodedFrameTestObserver() : length_(0),
+                                 frame_type_(kFrameEmpty),
+                                 called_(EventWrapper::Create()) {}
+    virtual ~EncodedFrameTestObserver() {}
+
+    virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
+      frame_type_ = encoded_frame.frame_type_;
+      length_ = encoded_frame.length_;
+      buffer_.reset(new uint8_t[length_]);
+      memcpy(buffer_.get(), encoded_frame.data_, length_);
+      called_->Set();
+    }
+
+    EventTypeWrapper Wait() {
+      return called_->Wait(kDefaultTimeoutMs);
+    }
+
+    void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
+      ASSERT_EQ(length_, observer.length_)
+          << "Observed frames are of different lengths.";
+      EXPECT_EQ(frame_type_, observer.frame_type_)
+          << "Observed frames have different frame types.";
+      EXPECT_EQ(0, memcmp(buffer_.get(), observer.buffer_.get(), length_))
+          << "Observed encoded frames have different content.";
+    }
+
+   private:
+    scoped_ptr<uint8_t[]> buffer_;
+    size_t length_;
+    FrameType frame_type_;
+    scoped_ptr<EventWrapper> called_;
+  };
+
+  EncodedFrameTestObserver post_encode_observer;
+  EncodedFrameTestObserver pre_decode_observer;
+
+  test::DirectTransport sender_transport, receiver_transport;
+
+  CreateCalls(Call::Config(&sender_transport),
+              Call::Config(&receiver_transport));
+
+  sender_transport.SetReceiver(receiver_call_->Receiver());
+  receiver_transport.SetReceiver(sender_call_->Receiver());
+
+  CreateTestConfigs();
+  send_config_.post_encode_callback = &post_encode_observer;
+  receive_config_.pre_decode_callback = &pre_decode_observer;
+
+  CreateStreams();
+  StartSending();
+
+  scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
+      send_config_.codec.width, send_config_.codec.height));
+  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+
+  EXPECT_EQ(kEventSignaled, post_encode_observer.Wait())
+      << "Timed out while waiting for send-side encoded-frame callback.";
+
+  EXPECT_EQ(kEventSignaled, pre_decode_observer.Wait())
+      << "Timed out while waiting for pre-decode encoded-frame callback.";
+
+  post_encode_observer.ExpectEqualFrames(pre_decode_observer);
+
+  StopSending();
+
+  sender_transport.StopSending();
+  receiver_transport.StopSending();
+
+  DestroyStreams();
+}
 }  // namespace webrtc
diff --git a/frame_callback.h b/frame_callback.h
index 7f54245..cfb07d8 100644
--- a/frame_callback.h
+++ b/frame_callback.h
@@ -11,11 +11,22 @@
 #ifndef WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
 #define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
 
+#include "webrtc/common_types.h"
+
 namespace webrtc {
 
 class I420VideoFrame;
 
-struct EncodedFrame;
+struct EncodedFrame {
+ public:
+  EncodedFrame() : data_(NULL), length_(0), frame_type_(kFrameEmpty) {}
+  EncodedFrame(const uint8_t* data, size_t length, FrameType frame_type)
+    : data_(data), length_(length), frame_type_(frame_type) {}
+
+  const uint8_t* data_;
+  const size_t length_;
+  const FrameType frame_type_;
+};
 
 class I420FrameCallback {
  public:
@@ -34,6 +45,7 @@
  protected:
   virtual ~EncodedFrameObserver() {}
 };
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
diff --git a/modules/video_coding/main/interface/video_coding.h b/modules/video_coding/main/interface/video_coding.h
index 9fb98c3..c016676 100644
--- a/modules/video_coding/main/interface/video_coding.h
+++ b/modules/video_coding/main/interface/video_coding.h
@@ -21,6 +21,7 @@
 {
 
 class Clock;
+class EncodedImageCallback;
 class VideoEncoder;
 class VideoDecoder;
 struct CodecSpecificInfo;
@@ -600,6 +601,11 @@
     // Returns true if SuspendBelowMinBitrate is engaged and the video has been
     // suspended due to bandwidth limitations; otherwise false.
     virtual bool VideoSuspended() const = 0;
+
+    virtual void RegisterPreDecodeImageCallback(
+        EncodedImageCallback* observer) = 0;
+    virtual void RegisterPostEncodeImageCallback(
+        EncodedImageCallback* post_encode_callback) = 0;
 };
 
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc
index c6ab9fb..064470b 100644
--- a/modules/video_coding/main/source/generic_encoder.cc
+++ b/modules/video_coding/main/source/generic_encoder.cc
@@ -12,6 +12,7 @@
 #include "webrtc/modules/video_coding/main/source/encoded_frame.h"
 #include "webrtc/modules/video_coding/main/source/generic_encoder.h"
 #include "webrtc/modules/video_coding/main/source/media_optimization.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
 
@@ -147,7 +148,9 @@
 _encodedBytes(0),
 _payloadType(0),
 _codecType(kVideoCodecUnknown),
-_internalSource(false)
+_internalSource(false),
+post_encode_callback_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+post_encode_callback_(NULL)
 #ifdef DEBUG_ENCODER_BIT_STREAM
 , _bitStreamAfterEncoder(NULL)
 #endif
@@ -177,6 +180,12 @@
     const CodecSpecificInfo* codecSpecificInfo,
     const RTPFragmentationHeader* fragmentationHeader)
 {
+    {
+      CriticalSectionScoped cs(post_encode_callback_lock_.get());
+      if (post_encode_callback_) {
+        post_encode_callback_->Encoded(encodedImage);
+      }
+    }
     FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
 
     uint32_t encodedBytes = 0;
@@ -271,4 +280,10 @@
       return;
   }
 }
+
+void VCMEncodedFrameCallback::RegisterPostEncodeImageCallback(
+    EncodedImageCallback* callback) {
+  CriticalSectionScoped cs(post_encode_callback_lock_.get());
+  post_encode_callback_ = callback;
+}
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.h b/modules/video_coding/main/source/generic_encoder.h
index 0c2d287..c5cfeab 100644
--- a/modules/video_coding/main/source/generic_encoder.h
+++ b/modules/video_coding/main/source/generic_encoder.h
@@ -15,9 +15,13 @@
 
 #include <stdio.h>
 
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
 namespace webrtc
 {
 
+class CriticalSectionWrapper;
+
 namespace media_optimization {
 class MediaOptimization;
 }  // namespace media_optimization
@@ -55,6 +59,8 @@
     void SetCodecType(VideoCodecType codecType) {_codecType = codecType;};
     void SetInternalSource(bool internalSource) { _internalSource = internalSource; };
 
+    void RegisterPostEncodeImageCallback(EncodedImageCallback* callback);
+
 private:
     /*
      * Map information from info into rtp. If no relevant information is found
@@ -69,6 +75,10 @@
     uint8_t _payloadType;
     VideoCodecType _codecType;
     bool _internalSource;
+
+    scoped_ptr<CriticalSectionWrapper> post_encode_callback_lock_;
+    EncodedImageCallback* post_encode_callback_;
+
 #ifdef DEBUG_ENCODER_BIT_STREAM
     FILE* _bitStreamAfterEncoder;
 #endif
diff --git a/modules/video_coding/main/source/video_coding_impl.cc b/modules/video_coding/main/source/video_coding_impl.cc
index 8282edc..1decc2f 100644
--- a/modules/video_coding/main/source/video_coding_impl.cc
+++ b/modules/video_coding/main/source/video_coding_impl.cc
@@ -319,6 +319,16 @@
     return receiver_->SetReceiveChannelParameters(rtt);
   }
 
+  virtual void RegisterPreDecodeImageCallback(
+      EncodedImageCallback* observer) OVERRIDE {
+    receiver_->RegisterPreDecodeImageCallback(observer);
+  }
+
+  virtual void RegisterPostEncodeImageCallback(
+      EncodedImageCallback* observer) OVERRIDE {
+    sender_->RegisterPostEncodeImageCallback(observer);
+  }
+
  private:
   scoped_ptr<vcm::VideoSender> sender_;
   scoped_ptr<vcm::VideoReceiver> receiver_;
diff --git a/modules/video_coding/main/source/video_coding_impl.h b/modules/video_coding/main/source/video_coding_impl.h
index c1d02d9..d9564c0 100644
--- a/modules/video_coding/main/source/video_coding_impl.h
+++ b/modules/video_coding/main/source/video_coding_impl.h
@@ -27,6 +27,9 @@
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
+
+class EncodedFrameObserver;
+
 namespace vcm {
 
 class VCMProcessTimer {
@@ -98,6 +101,9 @@
   void SuspendBelowMinBitrate();
   bool VideoSuspended() const;
 
+  void RegisterPostEncodeImageCallback(
+      EncodedImageCallback* post_encode_callback);
+
   int32_t TimeUntilNextProcess();
   int32_t Process();
 
@@ -173,6 +179,8 @@
   int32_t TimeUntilNextProcess();
   int32_t Process();
 
+  void RegisterPreDecodeImageCallback(EncodedImageCallback* observer);
+
  protected:
   int32_t Decode(const webrtc::VCMEncodedFrame& frame);
   int32_t RequestKeyFrame();
@@ -213,6 +221,7 @@
   VCMKeyRequestMode _keyRequestMode;
   bool _scheduleKeyRequest;
   size_t max_nack_list_size_;
+  EncodedImageCallback* pre_decode_image_callback_;
 
   VCMCodecDataBase _codecDataBase;
   VCMProcessTimer _receiveStatsTimer;
diff --git a/modules/video_coding/main/source/video_receiver.cc b/modules/video_coding/main/source/video_receiver.cc
index cea8b44..68668ea 100644
--- a/modules/video_coding/main/source/video_receiver.cc
+++ b/modules/video_coding/main/source/video_receiver.cc
@@ -52,6 +52,7 @@
       _keyRequestMode(kKeyOnError),
       _scheduleKeyRequest(false),
       max_nack_list_size_(0),
+      pre_decode_image_callback_(NULL),
       _codecDataBase(id),
       _receiveStatsTimer(1000, clock_),
       _retransmissionTimer(10, clock_),
@@ -400,6 +401,11 @@
     _timing.UpdateCurrentDelay(frame->RenderTimeMs(),
                                clock_->TimeInMilliseconds());
 
+    if (pre_decode_image_callback_) {
+      EncodedImage encoded_image(frame->EncodedImage());
+      pre_decode_image_callback_->Encoded(encoded_image);
+    }
+
 #ifdef DEBUG_DECODER_BIT_STREAM
     if (_bitStreamBeforeDecoder != NULL) {
       // Write bit stream to file for debugging purposes
@@ -815,5 +821,11 @@
   return _receiver.SetMinReceiverDelay(desired_delay_ms);
 }
 
+void VideoReceiver::RegisterPreDecodeImageCallback(
+    EncodedImageCallback* observer) {
+  CriticalSectionScoped cs(_receiveCritSect);
+  pre_decode_image_callback_ = observer;
+}
+
 }  // namespace vcm
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/video_sender.cc b/modules/video_coding/main/source/video_sender.cc
index 5a9a563..948218b 100644
--- a/modules/video_coding/main/source/video_sender.cc
+++ b/modules/video_coding/main/source/video_sender.cc
@@ -446,5 +446,11 @@
   return _mediaOpt.video_suspended();
 }
 
+void VideoSender::RegisterPostEncodeImageCallback(
+    EncodedImageCallback* observer) {
+  CriticalSectionScoped cs(_sendCritSect);
+  _encodedFrameCallback.RegisterPostEncodeImageCallback(observer);
+}
+
 }  // namespace vcm
 }  // namespace webrtc
diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc
index eec75be..f4e5227 100644
--- a/test/fake_encoder.cc
+++ b/test/fake_encoder.cc
@@ -20,13 +20,16 @@
       callback_(NULL),
       target_bitrate_kbps_(0),
       last_encode_time_ms_(0) {
-  memset(encoded_buffer_, 0, sizeof(encoded_buffer_));
+  // Generate some arbitrary not-all-zero data
+  for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) {
+    encoded_buffer_[i] = static_cast<uint8_t>(i);
+  }
 }
 
 FakeEncoder::~FakeEncoder() {}
 
 void FakeEncoder::SetCodecSettings(VideoCodec* codec,
-                                         size_t num_streams) {
+                                   size_t num_streams) {
   assert(num_streams > 0);
   assert(num_streams <= kMaxSimulcastStreams);
 
diff --git a/video/encoded_frame_callback_adapter.cc b/video/encoded_frame_callback_adapter.cc
new file mode 100644
index 0000000..f5eca7c
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.cc
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/video/encoded_frame_callback_adapter.h"
+
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
+
+namespace webrtc {
+namespace internal {
+
+EncodedFrameCallbackAdapter::EncodedFrameCallbackAdapter(
+    EncodedFrameObserver* observer) : observer_(observer) {
+}
+
+EncodedFrameCallbackAdapter::~EncodedFrameCallbackAdapter() {}
+
+int32_t EncodedFrameCallbackAdapter::Encoded(
+    EncodedImage& encodedImage,
+    const CodecSpecificInfo* codecSpecificInfo,
+    const RTPFragmentationHeader* fragmentation) {
+  assert(observer_ != NULL);
+  FrameType frame_type =
+        VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
+  const EncodedFrame frame(encodedImage._buffer,
+                           encodedImage._length,
+                           frame_type);
+  observer_->EncodedFrameCallback(frame);
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace webrtc
diff --git a/video/encoded_frame_callback_adapter.h b/video/encoded_frame_callback_adapter.h
new file mode 100644
index 0000000..d381479
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+#define WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/frame_callback.h"
+
+namespace webrtc {
+namespace internal {
+
+class EncodedFrameCallbackAdapter : public EncodedImageCallback {
+ public:
+  explicit EncodedFrameCallbackAdapter(EncodedFrameObserver* observer);
+  virtual ~EncodedFrameCallbackAdapter();
+
+  virtual int32_t Encoded(EncodedImage& encodedImage,
+                          const CodecSpecificInfo* codecSpecificInfo,
+                          const RTPFragmentationHeader* fragmentation);
+
+ private:
+  EncodedFrameObserver* observer_;
+};
+
+}  // namespace internal
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc
index d889379..5157237 100644
--- a/video/video_receive_stream.cc
+++ b/video/video_receive_stream.cc
@@ -32,7 +32,10 @@
                                        const VideoReceiveStream::Config& config,
                                        newapi::Transport* transport,
                                        webrtc::VoiceEngine* voice_engine)
-    : transport_adapter_(transport), config_(config), channel_(-1) {
+    : transport_adapter_(transport),
+      encoded_frame_proxy_(config.pre_decode_callback),
+      config_(config),
+      channel_(-1) {
   video_engine_base_ = ViEBase::GetInterface(video_engine);
   // TODO(mflodman): Use the other CreateChannel method.
   video_engine_base_->CreateChannel(channel_);
@@ -96,6 +99,10 @@
   }
 
   image_process_ = ViEImageProcess::GetInterface(video_engine);
+  if (config.pre_decode_callback) {
+    image_process_->RegisterPreDecodeImageCallback(channel_,
+                                                   &encoded_frame_proxy_);
+  }
   image_process_->RegisterPreRenderCallback(channel_,
                                             config_.pre_render_callback);
 
@@ -103,7 +110,8 @@
 }
 
 VideoReceiveStream::~VideoReceiveStream() {
-  image_process_->DeRegisterPreEncodeCallback(channel_);
+  image_process_->DeRegisterPreRenderCallback(channel_);
+  image_process_->DeRegisterPreDecodeCallback(channel_);
 
   render_->RemoveRenderer(channel_);
 
@@ -161,6 +169,5 @@
       video_frame, video_frame.render_time_ms() - clock_->TimeInMilliseconds());
   return 0;
 }
-
 }  // internal
 }  // webrtc
diff --git a/video/video_receive_stream.h b/video/video_receive_stream.h
index bf6f9db..8a23a10 100644
--- a/video/video_receive_stream.h
+++ b/video/video_receive_stream.h
@@ -16,6 +16,7 @@
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/modules/video_render/include/video_render_defines.h"
 #include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/video_engine/include/vie_render.h"
 #include "webrtc/video_receive_stream.h"
@@ -57,6 +58,7 @@
 
  private:
   TransportAdapter transport_adapter_;
+  EncodedFrameCallbackAdapter encoded_frame_proxy_;
   VideoReceiveStream::Config config_;
   Clock* clock_;
 
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 5b8c23c..b5fec72 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -83,6 +83,7 @@
                                  webrtc::VideoEngine* video_engine,
                                  const VideoSendStream::Config& config)
     : transport_adapter_(transport),
+      encoded_frame_proxy_(config.post_encode_callback),
       codec_lock_(CriticalSectionWrapper::CreateCriticalSection()),
       config_(config),
       external_codec_(NULL) {
@@ -199,6 +200,10 @@
   image_process_ = ViEImageProcess::GetInterface(video_engine);
   image_process_->RegisterPreEncodeCallback(channel_,
                                             config_.pre_encode_callback);
+  if (config_.post_encode_callback) {
+    image_process_->RegisterPostEncodeImageCallback(channel_,
+                                                    &encoded_frame_proxy_);
+  }
 
   if (config.suspend_below_min_bitrate) {
     codec_->SuspendBelowMinBitrate(channel_);
@@ -294,6 +299,5 @@
   return network_->ReceivedRTCPPacket(
              channel_, packet, static_cast<int>(length)) == 0;
 }
-
 }  // namespace internal
 }  // namespace webrtc
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index d0da69f..0881d91 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
@@ -62,6 +63,7 @@
 
  private:
   TransportAdapter transport_adapter_;
+  EncodedFrameCallbackAdapter encoded_frame_proxy_;
   scoped_ptr<CriticalSectionWrapper> codec_lock_;
   VideoSendStream::Config config_;
 
diff --git a/video/webrtc_video.gypi b/video/webrtc_video.gypi
index e26db0b..82e17f4 100644
--- a/video/webrtc_video.gypi
+++ b/video/webrtc_video.gypi
@@ -11,6 +11,8 @@
       '<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core',
     ],
     'webrtc_video_sources': [
+      'video/encoded_frame_callback_adapter.cc',
+      'video/encoded_frame_callback_adapter.h',
       'video/transport_adapter.cc',
       'video/transport_adapter.h',
       'video/video_receive_stream.cc',
diff --git a/video_engine/include/vie_image_process.h b/video_engine/include/vie_image_process.h
index 9a12748..5006eda 100644
--- a/video_engine/include/vie_image_process.h
+++ b/video_engine/include/vie_image_process.h
@@ -18,12 +18,11 @@
 #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_IMAGE_PROCESS_H_
 
 #include "webrtc/common_types.h"
-#include "webrtc/common_video/interface/i420_video_frame.h"
 
 namespace webrtc {
 
+class EncodedImageCallback;
 class I420FrameCallback;
-
 class VideoEngine;
 
 // This class declares an abstract interface for a user defined effect filter.
@@ -98,6 +97,16 @@
       I420FrameCallback* pre_encode_callback) = 0;
   virtual void DeRegisterPreEncodeCallback(int video_channel) = 0;
 
+  virtual void RegisterPostEncodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* post_encode_callback) = 0;
+  virtual void DeRegisterPostEncodeCallback(int video_channel) {}
+
+  virtual void RegisterPreDecodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* pre_decode_callback) = 0;
+  virtual void DeRegisterPreDecodeCallback(int video_channel) {}
+
   virtual void RegisterPreRenderCallback(
       int video_channel,
       I420FrameCallback* pre_render_callback) = 0;
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index aa86673..4a85a18 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -1870,6 +1870,12 @@
   pre_render_callback_ = pre_render_callback;
 }
 
+void ViEChannel::RegisterPreDecodeImageCallback(
+    EncodedImageCallback* pre_decode_callback) {
+  CriticalSectionScoped cs(callback_cs_.get());
+  vcm_.RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
 void ViEChannel::OnApplicationDataReceived(const int32_t id,
                                            const uint8_t sub_type,
                                            const uint32_t name,
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index 3b37140..f6bc3fc 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -33,6 +33,7 @@
 class ChannelStatsObserver;
 class Config;
 class CriticalSectionWrapper;
+class EncodedImageCallback;
 class Encryption;
 class I420FrameCallback;
 class PacedSender;
@@ -320,8 +321,10 @@
 
   int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter);
 
-  // New-style callback, used by VideoReceiveStream.
+  // New-style callbacks, used by VideoReceiveStream.
   void RegisterPreRenderCallback(I420FrameCallback* pre_render_callback);
+  void RegisterPreDecodeImageCallback(
+      EncodedImageCallback* pre_decode_callback);
 
  protected:
   static bool ChannelDecodeThreadFunction(void* obj);
diff --git a/video_engine/vie_encoder.cc b/video_engine/vie_encoder.cc
index 9733a89..ce13b1a 100644
--- a/video_engine/vie_encoder.cc
+++ b/video_engine/vie_encoder.cc
@@ -14,6 +14,7 @@
 
 #include <algorithm>
 
+#include "webrtc/common_video/interface/video_image.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/modules/pacing/include/paced_sender.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@@ -21,6 +22,7 @@
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
@@ -1183,6 +1185,15 @@
   pre_encode_callback_ = NULL;
 }
 
+void ViEEncoder::RegisterPostEncodeImageCallback(
+      EncodedImageCallback* post_encode_callback) {
+  vcm_.RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEEncoder::DeRegisterPostEncodeImageCallback() {
+  vcm_.RegisterPostEncodeImageCallback(NULL);
+}
+
 QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessingModule* vpm)
     : vpm_(vpm) {
 }
diff --git a/video_engine/vie_encoder.h b/video_engine/vie_encoder.h
index 73f84af..cd8328e 100644
--- a/video_engine/vie_encoder.h
+++ b/video_engine/vie_encoder.h
@@ -29,6 +29,7 @@
 
 class Config;
 class CriticalSectionWrapper;
+class EncodedImageCallback;
 class PacedSender;
 class ProcessThread;
 class QMVideoSettingsCallback;
@@ -168,11 +169,15 @@
   // |threshold_bps| + |window_bps|.
   virtual void SuspendBelowMinBitrate();
 
-  // New-style callback, used by VideoSendStream.
+  // New-style callbacks, used by VideoSendStream.
   void RegisterPreEncodeCallback(I420FrameCallback* pre_encode_callback);
   void DeRegisterPreEncodeCallback();
+  void RegisterPostEncodeImageCallback(
+        EncodedImageCallback* post_encode_callback);
+  void DeRegisterPostEncodeImageCallback();
 
   int channel_id() const { return channel_id_; }
+
  protected:
   // Called by BitrateObserver.
   void OnNetworkChanged(const uint32_t bitrate_bps,
diff --git a/video_engine/vie_image_process_impl.cc b/video_engine/vie_image_process_impl.cc
index 92fe697..b10e774 100644
--- a/video_engine/vie_image_process_impl.cc
+++ b/video_engine/vie_image_process_impl.cc
@@ -284,6 +284,36 @@
   vie_encoder->DeRegisterPreEncodeCallback();
 }
 
+void ViEImageProcessImpl::RegisterPostEncodeImageCallback(
+    int video_channel,
+    EncodedImageCallback* post_encode_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder != NULL);
+  vie_encoder->RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPostEncodeCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder != NULL);
+  vie_encoder->DeRegisterPostEncodeImageCallback();
+}
+
+void ViEImageProcessImpl::RegisterPreDecodeImageCallback(
+    int video_channel,
+    EncodedImageCallback* pre_decode_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* channel = cs.Channel(video_channel);
+  channel->RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPreDecodeCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* channel = cs.Channel(video_channel);
+  channel->RegisterPreDecodeImageCallback(NULL);
+}
+
 void ViEImageProcessImpl::RegisterPreRenderCallback(
     int video_channel,
     I420FrameCallback* pre_render_callback) {
diff --git a/video_engine/vie_image_process_impl.h b/video_engine/vie_image_process_impl.h
index 9b43e7e..74a7ff0 100644
--- a/video_engine/vie_image_process_impl.h
+++ b/video_engine/vie_image_process_impl.h
@@ -43,6 +43,16 @@
       I420FrameCallback* pre_encode_callback) OVERRIDE;
   virtual void DeRegisterPreEncodeCallback(int video_channel) OVERRIDE;
 
+  virtual void RegisterPostEncodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* post_encode_callback) OVERRIDE;
+  virtual void DeRegisterPostEncodeCallback(int video_channel) OVERRIDE;
+
+  virtual void RegisterPreDecodeImageCallback(
+        int video_channel,
+        EncodedImageCallback* post_encode_callback) OVERRIDE;
+  virtual void DeRegisterPreDecodeCallback(int video_channel) OVERRIDE;
+
   virtual void RegisterPreRenderCallback(
       int video_channel,
       I420FrameCallback* pre_render_callback) OVERRIDE;
diff --git a/video_send_stream.h b/video_send_stream.h
index 85d7519..2358042 100644
--- a/video_send_stream.h
+++ b/video_send_stream.h
@@ -71,7 +71,7 @@
   struct Config {
     Config()
         : pre_encode_callback(NULL),
-          encoded_callback(NULL),
+          post_encode_callback(NULL),
           local_renderer(NULL),
           render_delay_ms(0),
           encoder(NULL),
@@ -113,7 +113,7 @@
 
     // Called for each encoded frame, e.g. used for file storage. 'NULL'
     // disables the callback.
-    EncodedFrameObserver* encoded_callback;
+    EncodedFrameObserver* post_encode_callback;
 
     // Renderer for local preview. The local renderer will be called even if
     // sending hasn't started. 'NULL' disables local rendering.