Implement I420FrameCallbacks in Call.

BUG=2425
R=mflodman@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@5005 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/video_engine/include/vie_image_process.h b/video_engine/include/vie_image_process.h
index cb66bb1..9a12748 100644
--- a/video_engine/include/vie_image_process.h
+++ b/video_engine/include/vie_image_process.h
@@ -22,6 +22,8 @@
 
 namespace webrtc {
 
+class I420FrameCallback;
+
 class VideoEngine;
 
 // This class declares an abstract interface for a user defined effect filter.
@@ -90,6 +92,17 @@
   virtual int EnableColorEnhancement(const int video_channel,
                                      const bool enable) = 0;
 
+  // New-style callbacks, used by VideoSendStream/VideoReceiveStream.
+  virtual void RegisterPreEncodeCallback(
+      int video_channel,
+      I420FrameCallback* pre_encode_callback) = 0;
+  virtual void DeRegisterPreEncodeCallback(int video_channel) = 0;
+
+  virtual void RegisterPreRenderCallback(
+      int video_channel,
+      I420FrameCallback* pre_render_callback) = 0;
+  virtual void DeRegisterPreRenderCallback(int video_channel) = 0;
+
  protected:
   ViEImageProcess() {}
   virtual ~ViEImageProcess() {}
diff --git a/video_engine/internal/video_receive_stream.cc b/video_engine/internal/video_receive_stream.cc
index c92cc8d..a6a5875 100644
--- a/video_engine/internal/video_receive_stream.cc
+++ b/video_engine/internal/video_receive_stream.cc
@@ -19,6 +19,7 @@
 #include "webrtc/video_engine/include/vie_capture.h"
 #include "webrtc/video_engine/include/vie_codec.h"
 #include "webrtc/video_engine/include/vie_external_codec.h"
+#include "webrtc/video_engine/include/vie_image_process.h"
 #include "webrtc/video_engine/include/vie_network.h"
 #include "webrtc/video_engine/include/vie_render.h"
 #include "webrtc/video_engine/include/vie_rtp_rtcp.h"
@@ -86,21 +87,28 @@
   render_ = webrtc::ViERender::GetInterface(video_engine);
   assert(render_ != NULL);
 
-  if (render_->AddRenderer(channel_, kVideoI420, this) != 0) {
-    abort();
-  }
+  render_->AddRenderer(channel_, kVideoI420, this);
+
+  image_process_ = ViEImageProcess::GetInterface(video_engine);
+  image_process_->RegisterPreRenderCallback(channel_,
+                                            config_.pre_render_callback);
 
   clock_ = Clock::GetRealTimeClock();
 }
 
 VideoReceiveStream::~VideoReceiveStream() {
+  image_process_->DeRegisterPreEncodeCallback(channel_);
+
   render_->RemoveRenderer(channel_);
+
   for (size_t i = 0; i < config_.external_decoders.size(); ++i) {
     external_codec_->DeRegisterExternalReceiveCodec(
         channel_, config_.external_decoders[i].payload_type);
   }
+
   network_->DeregisterSendTransport(channel_);
 
+  image_process_->Release();
   video_engine_base_->Release();
   external_codec_->Release();
   codec_->Release();
@@ -170,15 +178,8 @@
   video_frame.set_timestamp(timestamp);
   video_frame.set_render_time_ms(render_time);
 
-  if (config_.post_decode_callback != NULL) {
-    config_.post_decode_callback->FrameCallback(&video_frame);
-  }
-
-  if (config_.renderer != NULL) {
-    // TODO(pbos): Add timing to RenderFrame call
-    config_.renderer->RenderFrame(video_frame,
-                                  render_time - clock_->TimeInMilliseconds());
-  }
+  config_.renderer->RenderFrame(video_frame,
+                                render_time - clock_->TimeInMilliseconds());
 
   return 0;
 }
diff --git a/video_engine/internal/video_receive_stream.h b/video_engine/internal/video_receive_stream.h
index b6b5319..b5d095c 100644
--- a/video_engine/internal/video_receive_stream.h
+++ b/video_engine/internal/video_receive_stream.h
@@ -25,6 +25,7 @@
 class ViEBase;
 class ViECodec;
 class ViEExternalCodec;
+class ViEImageProcess;
 class ViENetwork;
 class ViERender;
 class ViERTP_RTCP;
@@ -66,6 +67,7 @@
   ViENetwork* network_;
   ViERender* render_;
   ViERTP_RTCP* rtp_rtcp_;
+  ViEImageProcess* image_process_;
 
   int channel_;
 
diff --git a/video_engine/internal/video_send_stream.cc b/video_engine/internal/video_send_stream.cc
index 55921c2..d238b95 100644
--- a/video_engine/internal/video_send_stream.cc
+++ b/video_engine/internal/video_send_stream.cc
@@ -19,6 +19,7 @@
 #include "webrtc/video_engine/include/vie_capture.h"
 #include "webrtc/video_engine/include/vie_codec.h"
 #include "webrtc/video_engine/include/vie_external_codec.h"
+#include "webrtc/video_engine/include/vie_image_process.h"
 #include "webrtc/video_engine/include/vie_network.h"
 #include "webrtc/video_engine/include/vie_rtp_rtcp.h"
 #include "webrtc/video_engine/new_include/video_send_stream.h"
@@ -190,11 +191,16 @@
     video_engine_base_->RegisterCpuOveruseObserver(channel_,
                                                    overuse_observer_.get());
   }
+
+  image_process_ = ViEImageProcess::GetInterface(video_engine);
+  image_process_->RegisterPreEncodeCallback(channel_,
+                                            config_.pre_encode_callback);
 }
 
 VideoSendStream::~VideoSendStream() {
+  image_process_->DeRegisterPreEncodeCallback(channel_);
+
   network_->DeregisterSendTransport(channel_);
-  video_engine_base_->DeleteChannel(channel_);
 
   capture_->DisconnectCaptureDevice(channel_);
   capture_->ReleaseCaptureDevice(capture_id_);
@@ -204,6 +210,9 @@
                                                  config_.codec.plType);
   }
 
+  video_engine_base_->DeleteChannel(channel_);
+
+  image_process_->Release();
   video_engine_base_->Release();
   capture_->Release();
   codec_->Release();
@@ -220,10 +229,6 @@
   I420VideoFrame frame_copy;
   frame_copy.CopyFrame(frame);
 
-  if (config_.pre_encode_callback != NULL) {
-    config_.pre_encode_callback->FrameCallback(&frame_copy);
-  }
-
   ViEVideoFrameI420 vf;
 
   // TODO(pbos): This represents a memcpy step and is only required because
diff --git a/video_engine/internal/video_send_stream.h b/video_engine/internal/video_send_stream.h
index 1241b48..27027ef 100644
--- a/video_engine/internal/video_send_stream.h
+++ b/video_engine/internal/video_send_stream.h
@@ -26,6 +26,7 @@
 class ViECodec;
 class ViEExternalCapture;
 class ViEExternalCodec;
+class ViEImageProcess;
 class ViENetwork;
 class ViERTP_RTCP;
 
@@ -72,6 +73,7 @@
   ViEExternalCodec* external_codec_;
   ViENetwork* network_;
   ViERTP_RTCP* rtp_rtcp_;
+  ViEImageProcess* image_process_;
 
   int channel_;
   int capture_id_;
diff --git a/video_engine/new_include/video_receive_stream.h b/video_engine/new_include/video_receive_stream.h
index ba0f260..b5b47c5 100644
--- a/video_engine/new_include/video_receive_stream.h
+++ b/video_engine/new_include/video_receive_stream.h
@@ -98,7 +98,7 @@
           render_delay_ms(0),
           audio_channel_id(0),
           pre_decode_callback(NULL),
-          post_decode_callback(NULL),
+          pre_render_callback(NULL),
           target_delay_ms(0) {}
     // Codecs the receive stream can receive.
     std::vector<VideoCodec> codecs;
@@ -150,7 +150,7 @@
     // Called for each decoded frame. E.g. used when adding effects to the
     // decoded
     // stream. 'NULL' disables the callback.
-    I420FrameCallback* post_decode_callback;
+    I420FrameCallback* pre_render_callback;
 
     // External video decoders to be used if incoming payload type matches the
     // registered type for an external decoder.
diff --git a/video_engine/test/call_tests.cc b/video_engine/test/call_tests.cc
index 207aaf4..49cb8f5 100644
--- a/video_engine/test/call_tests.cc
+++ b/video_engine/test/call_tests.cc
@@ -236,7 +236,7 @@
 TEST_F(CallTest, UsesTraceCallback) {
   const unsigned int kSenderTraceFilter = kTraceDebug;
   const unsigned int kReceiverTraceFilter = kTraceDefault & (~kTraceDebug);
-  class TraceObserver: public TraceCallback {
+  class TraceObserver : public TraceCallback {
    public:
     TraceObserver(unsigned int filter)
         : filter_(filter), messages_left_(50), done_(EventWrapper::Create()) {}
@@ -347,7 +347,6 @@
 
   CreateStreams();
   CreateFrameGenerator();
-
   StartSending();
 
   // Wait() waits for an event triggered when NACKs have been received, NACKed
@@ -361,6 +360,103 @@
   DestroyStreams();
 }
 
+TEST_F(CallTest, UsesFrameCallbacks) {
+  static const int kWidth = 320;
+  static const int kHeight = 240;
+
+  class Renderer : public VideoRenderer {
+   public:
+    Renderer() : event_(EventWrapper::Create()) {}
+
+    virtual void RenderFrame(const I420VideoFrame& video_frame,
+                             int /*time_to_render_ms*/) OVERRIDE {
+      EXPECT_EQ(0, *video_frame.buffer(kYPlane))
+          << "Rendered frame should have zero luma which is applied by the "
+             "pre-render callback.";
+      event_->Set();
+    }
+
+    EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); }
+    scoped_ptr<EventWrapper> event_;
+  } renderer;
+
+  class TestFrameCallback : public I420FrameCallback {
+   public:
+    TestFrameCallback(int expected_luma_byte, int next_luma_byte)
+        : event_(EventWrapper::Create()),
+          expected_luma_byte_(expected_luma_byte),
+          next_luma_byte_(next_luma_byte) {}
+
+    EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); }
+
+   private:
+    virtual void FrameCallback(I420VideoFrame* frame) {
+      EXPECT_EQ(kWidth, frame->width())
+          << "Width not as expected, callback done before resize?";
+      EXPECT_EQ(kHeight, frame->height())
+          << "Height not as expected, callback done before resize?";
+
+      // Previous luma specified, observed luma should be fairly close.
+      if (expected_luma_byte_ != -1) {
+        EXPECT_NEAR(expected_luma_byte_, *frame->buffer(kYPlane), 10);
+      }
+
+      memset(frame->buffer(kYPlane),
+             next_luma_byte_,
+             frame->allocated_size(kYPlane));
+
+      event_->Set();
+    }
+
+    scoped_ptr<EventWrapper> event_;
+    int expected_luma_byte_;
+    int next_luma_byte_;
+  };
+
+  TestFrameCallback pre_encode_callback(-1, 255);  // Changes luma to 255.
+  TestFrameCallback pre_render_callback(255, 0);  // Changes luma from 255 to 0.
+
+  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_.encoder = NULL;
+  send_config_.codec = sender_call_->GetVideoCodecs()[0];
+  send_config_.codec.width = kWidth;
+  send_config_.codec.height = kHeight;
+  send_config_.pre_encode_callback = &pre_encode_callback;
+  receive_config_.pre_render_callback = &pre_render_callback;
+  receive_config_.renderer = &renderer;
+
+  CreateStreams();
+  StartSending();
+
+  // Create frames that are smaller than the send width/height, this is done to
+  // check that the callbacks are done after processing video.
+  scoped_ptr<test::FrameGenerator> frame_generator(
+      test::FrameGenerator::Create(kWidth / 2, kHeight / 2));
+  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+
+  EXPECT_EQ(kEventSignaled, pre_encode_callback.Wait())
+      << "Timed out while waiting for pre-encode callback.";
+  EXPECT_EQ(kEventSignaled, pre_render_callback.Wait())
+      << "Timed out while waiting for pre-render callback.";
+  EXPECT_EQ(kEventSignaled, renderer.Wait())
+      << "Timed out while waiting for the frame to render.";
+
+  StopSending();
+
+  sender_transport.StopSending();
+  receiver_transport.StopSending();
+
+  DestroyStreams();
+}
+
 class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
   static const int kInverseDropProbability = 16;
 
@@ -450,7 +546,6 @@
 
   CreateStreams();
   CreateFrameGenerator();
-
   StartSending();
 
   // Wait() waits for an event triggered when Pli has been received and frames
@@ -510,7 +605,6 @@
 
   CreateStreams();
   CreateFrameGenerator();
-
   StartSending();
 
   receiver_call_->DestroyReceiveStream(receive_stream_);
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index e80cb4b..10f46eb 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -29,6 +29,7 @@
 #include "webrtc/video_engine/include/vie_errors.h"
 #include "webrtc/video_engine/include/vie_image_process.h"
 #include "webrtc/video_engine/include/vie_rtp_rtcp.h"
+#include "webrtc/video_engine/new_include/frame_callback.h"
 #include "webrtc/video_engine/vie_defines.h"
 
 namespace webrtc {
@@ -101,7 +102,8 @@
       mtu_(0),
       sender_(sender),
       nack_history_size_sender_(kSendSidePacketHistorySize),
-      max_nack_reordering_threshold_(kMaxPacketAgeToNack) {
+      max_nack_reordering_threshold_(kMaxPacketAgeToNack),
+      pre_render_callback_(NULL) {
   WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id),
                "ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)",
                channel_id, engine_id);
@@ -1599,6 +1601,8 @@
   }
   // Post processing is not supported if the frame is backed by a texture.
   if (video_frame.native_handle() == NULL) {
+    if (pre_render_callback_ != NULL)
+      pre_render_callback_->FrameCallback(&video_frame);
     if (effect_filter_) {
       unsigned int length = CalcBufferSize(kI420,
                                            video_frame.width(),
@@ -1827,6 +1831,12 @@
   return 0;
 }
 
+void ViEChannel::RegisterPreRenderCallback(
+    I420FrameCallback* pre_render_callback) {
+  CriticalSectionScoped cs(callback_cs_.get());
+  pre_render_callback_ = pre_render_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 9791e10..40ceb1c 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -34,6 +34,7 @@
 class Config;
 class CriticalSectionWrapper;
 class Encryption;
+class I420FrameCallback;
 class PacedSender;
 class ProcessThread;
 class RtcpRttObserver;
@@ -308,6 +309,9 @@
 
   int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter);
 
+  // New-style callback, used by VideoReceiveStream.
+  void RegisterPreRenderCallback(I420FrameCallback* pre_render_callback);
+
  protected:
   static bool ChannelDecodeThreadFunction(void* obj);
   bool ChannelDecodeProcess();
@@ -384,6 +388,7 @@
 
   int nack_history_size_sender_;
   int max_nack_reordering_threshold_;
+  I420FrameCallback* pre_render_callback_;
 };
 
 }  // namespace webrtc
diff --git a/video_engine/vie_encoder.cc b/video_engine/vie_encoder.cc
index 1ca8c50..eb75d62 100644
--- a/video_engine/vie_encoder.cc
+++ b/video_engine/vie_encoder.cc
@@ -28,6 +28,7 @@
 #include "webrtc/system_wrappers/interface/trace_event.h"
 #include "webrtc/video_engine/include/vie_codec.h"
 #include "webrtc/video_engine/include/vie_image_process.h"
+#include "webrtc/video_engine/new_include/frame_callback.h"
 #include "webrtc/video_engine/vie_defines.h"
 
 namespace webrtc {
@@ -162,7 +163,8 @@
     has_received_rpsi_(false),
     picture_id_rpsi_(0),
     qm_callback_(NULL),
-    video_auto_muted_(false) {
+    video_auto_muted_(false),
+    pre_encode_callback_(NULL) {
   WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo,
                ViEId(engine_id, channel_id),
                "%s(engine_id: %d) 0x%p - Constructor", __FUNCTION__, engine_id,
@@ -650,6 +652,13 @@
   if (decimated_frame == NULL)  {
     decimated_frame = video_frame;
   }
+
+  {
+    CriticalSectionScoped cs(callback_cs_.get());
+    if (pre_encode_callback_)
+      pre_encode_callback_->FrameCallback(decimated_frame);
+  }
+
 #ifdef VIDEOCODEC_VP8
   if (vcm_.SendCodec() == webrtc::kVideoCodecVP8) {
     webrtc::CodecSpecificInfo codec_specific_info;
@@ -1152,6 +1161,17 @@
   vcm_.EnableAutoMuting(threshold_bps, window_bps);
 }
 
+void ViEEncoder::RegisterPreEncodeCallback(
+    I420FrameCallback* pre_encode_callback) {
+  CriticalSectionScoped cs(callback_cs_.get());
+  pre_encode_callback_ = pre_encode_callback;
+}
+
+void ViEEncoder::DeRegisterPreEncodeCallback() {
+  CriticalSectionScoped cs(callback_cs_.get());
+  pre_encode_callback_ = NULL;
+}
+
 QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessingModule* vpm)
     : vpm_(vpm) {
 }
diff --git a/video_engine/vie_encoder.h b/video_engine/vie_encoder.h
index f679062..bb60699 100644
--- a/video_engine/vie_encoder.h
+++ b/video_engine/vie_encoder.h
@@ -21,6 +21,7 @@
 #include "webrtc/modules/video_processing/main/interface/video_processing.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/typedefs.h"
+#include "webrtc/video_engine/new_include/frame_callback.h"
 #include "webrtc/video_engine/vie_defines.h"
 #include "webrtc/video_engine/vie_frame_provider_base.h"
 
@@ -167,6 +168,10 @@
   // |threshold_bps| + |window_bps|.
   virtual void EnableAutoMuting(int threshold_bps, int window_bps);
 
+  // New-style callback, used by VideoSendStream.
+  void RegisterPreEncodeCallback(I420FrameCallback* pre_encode_callback);
+  void DeRegisterPreEncodeCallback();
+
   int channel_id() const { return channel_id_; }
  protected:
   // Called by BitrateObserver.
@@ -178,7 +183,6 @@
   bool TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number,
                         int64_t capture_time_ms);
   int TimeToSendPadding(int bytes);
-
  private:
   bool EncoderPaused() const;
 
@@ -223,6 +227,7 @@
   // Quality modes callback
   QMVideoSettingsCallback* qm_callback_;
   bool video_auto_muted_;
+  I420FrameCallback* pre_encode_callback_;
 };
 
 }  // namespace webrtc
diff --git a/video_engine/vie_image_process_impl.cc b/video_engine/vie_image_process_impl.cc
index 199073a..92fe697 100644
--- a/video_engine/vie_image_process_impl.cc
+++ b/video_engine/vie_image_process_impl.cc
@@ -269,4 +269,35 @@
   return 0;
 }
 
+void ViEImageProcessImpl::RegisterPreEncodeCallback(
+    int video_channel,
+    I420FrameCallback* pre_encode_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  vie_encoder->RegisterPreEncodeCallback(pre_encode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPreEncodeCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder != NULL);
+  vie_encoder->DeRegisterPreEncodeCallback();
+}
+
+void ViEImageProcessImpl::RegisterPreRenderCallback(
+    int video_channel,
+    I420FrameCallback* pre_render_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterPreRenderCallback(pre_render_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPreRenderCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterPreRenderCallback(NULL);
+}
+
 }  // namespace webrtc
diff --git a/video_engine/vie_image_process_impl.h b/video_engine/vie_image_process_impl.h
index 81e6b78..9b43e7e 100644
--- a/video_engine/vie_image_process_impl.h
+++ b/video_engine/vie_image_process_impl.h
@@ -38,6 +38,15 @@
   virtual int EnableDenoising(const int capture_id, const bool enable);
   virtual int EnableColorEnhancement(const int video_channel,
                                      const bool enable);
+  virtual void RegisterPreEncodeCallback(
+      int video_channel,
+      I420FrameCallback* pre_encode_callback) OVERRIDE;
+  virtual void DeRegisterPreEncodeCallback(int video_channel) OVERRIDE;
+
+  virtual void RegisterPreRenderCallback(
+      int video_channel,
+      I420FrameCallback* pre_render_callback) OVERRIDE;
+  virtual void DeRegisterPreRenderCallback(int video_channel) OVERRIDE;
 
  protected:
   explicit ViEImageProcessImpl(ViESharedData* shared_data);