1/*
2 * libjingle
3 * Copyright 2011 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifndef TALK_MEDIA_BASE_FAKEVIDEORENDERER_H_
29#define TALK_MEDIA_BASE_FAKEVIDEORENDERER_H_
30
31#include "talk/media/base/videoframe.h"
32#include "talk/media/base/videorenderer.h"
33#include "webrtc/base/logging.h"
34#include "webrtc/base/sigslot.h"
35
36namespace cricket {
37
38// Faked video renderer that has a callback for actions on rendering.
39class FakeVideoRenderer : public VideoRenderer {
40 public:
41  FakeVideoRenderer()
42      : errors_(0),
43        width_(0),
44        height_(0),
45        num_set_sizes_(0),
46        num_rendered_frames_(0),
47        black_frame_(false) {
48  }
49
50  virtual bool SetSize(int width, int height, int reserved) {
51    rtc::CritScope cs(&crit_);
52    width_ = width;
53    height_ = height;
54    ++num_set_sizes_;
55    SignalSetSize(width, height, reserved);
56    return true;
57  }
58
59  virtual bool RenderFrame(const VideoFrame* frame) {
60    rtc::CritScope cs(&crit_);
61    // TODO(zhurunz) Check with VP8 team to see if we can remove this
62    // tolerance on Y values.
63    black_frame_ = CheckFrameColorYuv(6, 48, 128, 128, 128, 128, frame);
64    // Treat unexpected frame size as error.
65    if (!frame ||
66        frame->GetWidth() != static_cast<size_t>(width_) ||
67        frame->GetHeight() != static_cast<size_t>(height_)) {
68      if (!frame) {
69        LOG(LS_WARNING) << "RenderFrame expected non-null frame.";
70      } else {
71        LOG(LS_WARNING) << "RenderFrame expected frame of size " << width_
72                        << "x" << height_ << " but received frame of size "
73                        << frame->GetWidth() << "x" << frame->GetHeight();
74      }
75      ++errors_;
76      return false;
77    }
78    ++num_rendered_frames_;
79    SignalRenderFrame(frame);
80    return true;
81  }
82
83  int errors() const { return errors_; }
84  int width() const {
85    rtc::CritScope cs(&crit_);
86    return width_;
87  }
88  int height() const {
89    rtc::CritScope cs(&crit_);
90    return height_;
91  }
92  int num_set_sizes() const {
93    rtc::CritScope cs(&crit_);
94    return num_set_sizes_;
95  }
96  int num_rendered_frames() const {
97    rtc::CritScope cs(&crit_);
98    return num_rendered_frames_;
99  }
100  bool black_frame() const {
101    rtc::CritScope cs(&crit_);
102    return black_frame_;
103  }
104
105  sigslot::signal3<int, int, int> SignalSetSize;
106  sigslot::signal1<const VideoFrame*> SignalRenderFrame;
107
108 private:
109  static bool CheckFrameColorYuv(uint8_t y_min,
110                                 uint8_t y_max,
111                                 uint8_t u_min,
112                                 uint8_t u_max,
113                                 uint8_t v_min,
114                                 uint8_t v_max,
115                                 const cricket::VideoFrame* frame) {
116    if (!frame) {
117      return false;
118    }
119    // Y
120    size_t y_width = frame->GetWidth();
121    size_t y_height = frame->GetHeight();
122    const uint8_t* y_plane = frame->GetYPlane();
123    const uint8_t* y_pos = y_plane;
124    int32_t y_pitch = frame->GetYPitch();
125    for (size_t i = 0; i < y_height; ++i) {
126      for (size_t j = 0; j < y_width; ++j) {
127        uint8_t y_value = *(y_pos + j);
128        if (y_value < y_min || y_value > y_max) {
129          return false;
130        }
131      }
132      y_pos += y_pitch;
133    }
134    // U and V
135    size_t chroma_width = frame->GetChromaWidth();
136    size_t chroma_height = frame->GetChromaHeight();
137    const uint8_t* u_plane = frame->GetUPlane();
138    const uint8_t* v_plane = frame->GetVPlane();
139    const uint8_t* u_pos = u_plane;
140    const uint8_t* v_pos = v_plane;
141    int32_t u_pitch = frame->GetUPitch();
142    int32_t v_pitch = frame->GetVPitch();
143    for (size_t i = 0; i < chroma_height; ++i) {
144      for (size_t j = 0; j < chroma_width; ++j) {
145        uint8_t u_value = *(u_pos + j);
146        if (u_value < u_min || u_value > u_max) {
147          return false;
148        }
149        uint8_t v_value = *(v_pos + j);
150        if (v_value < v_min || v_value > v_max) {
151          return false;
152        }
153      }
154      u_pos += u_pitch;
155      v_pos += v_pitch;
156    }
157    return true;
158  }
159
160  int errors_;
161  int width_;
162  int height_;
163  int num_set_sizes_;
164  int num_rendered_frames_;
165  bool black_frame_;
166  mutable rtc::CriticalSection crit_;
167};
168
169}  // namespace cricket
170
171#endif  // TALK_MEDIA_BASE_FAKEVIDEORENDERER_H_
172