1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/video_engine/vie_frame_provider_base.h"
12
13#include <algorithm>
14
15#include "webrtc/common_video/interface/i420_video_frame.h"
16#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
17#include "webrtc/system_wrappers/interface/logging.h"
18#include "webrtc/system_wrappers/interface/tick_util.h"
19#include "webrtc/video_engine/vie_defines.h"
20
21namespace webrtc {
22
23ViEFrameProviderBase::ViEFrameProviderBase(int Id, int engine_id)
24    : id_(Id),
25      engine_id_(engine_id),
26      provider_cs_(CriticalSectionWrapper::CreateCriticalSection()),
27      frame_delay_(0) {
28}
29
30ViEFrameProviderBase::~ViEFrameProviderBase() {
31  if (frame_callbacks_.size() > 0) {
32    LOG_F(LS_WARNING) << "FrameCallbacks still exist when Provider deleted: "
33                      << frame_callbacks_.size();
34  }
35
36  for (FrameCallbacks::iterator it = frame_callbacks_.begin();
37       it != frame_callbacks_.end(); ++it) {
38    (*it)->ProviderDestroyed(id_);
39  }
40  frame_callbacks_.clear();
41}
42
43int ViEFrameProviderBase::Id() {
44  return id_;
45}
46
47void ViEFrameProviderBase::DeliverFrame(
48    I420VideoFrame* video_frame,
49    int num_csrcs,
50    const uint32_t CSRC[kRtpCsrcSize]) {
51#ifdef DEBUG_
52  const TickTime start_process_time = TickTime::Now();
53#endif
54  CriticalSectionScoped cs(provider_cs_.get());
55
56  // Deliver the frame to all registered callbacks.
57  if (frame_callbacks_.size() > 0) {
58    if (frame_callbacks_.size() == 1) {
59      // We don't have to copy the frame.
60      frame_callbacks_.front()->DeliverFrame(id_, video_frame, num_csrcs, CSRC);
61    } else {
62      for (FrameCallbacks::iterator it = frame_callbacks_.begin();
63           it != frame_callbacks_.end(); ++it) {
64        if (video_frame->native_handle() != NULL) {
65          (*it)->DeliverFrame(id_, video_frame, num_csrcs, CSRC);
66        } else {
67          // Make a copy of the frame for all callbacks.
68          if (!extra_frame_.get()) {
69            extra_frame_.reset(new I420VideoFrame());
70          }
71          extra_frame_->CopyFrame(*video_frame);
72          (*it)->DeliverFrame(id_, extra_frame_.get(), num_csrcs, CSRC);
73        }
74      }
75    }
76  }
77#ifdef DEBUG_
78  const int process_time =
79      static_cast<int>((TickTime::Now() - start_process_time).Milliseconds());
80  if (process_time > 25) {
81    // Warn if the delivery time is too long.
82    LOG(LS_WARNING) << "Too long time delivering frame " << process_time;
83  }
84#endif
85}
86
87void ViEFrameProviderBase::SetFrameDelay(int frame_delay) {
88  CriticalSectionScoped cs(provider_cs_.get());
89  frame_delay_ = frame_delay;
90
91  for (FrameCallbacks::iterator it = frame_callbacks_.begin();
92       it != frame_callbacks_.end(); ++it) {
93    (*it)->DelayChanged(id_, frame_delay);
94  }
95}
96
97int ViEFrameProviderBase::FrameDelay() {
98  return frame_delay_;
99}
100
101int ViEFrameProviderBase::GetBestFormat(int* best_width,
102                                        int* best_height,
103                                        int* best_frame_rate) {
104  int largest_width = 0;
105  int largest_height = 0;
106  int highest_frame_rate = 0;
107
108  CriticalSectionScoped cs(provider_cs_.get());
109  for (FrameCallbacks::iterator it = frame_callbacks_.begin();
110       it != frame_callbacks_.end(); ++it) {
111    int prefered_width = 0;
112    int prefered_height = 0;
113    int prefered_frame_rate = 0;
114    if ((*it)->GetPreferedFrameSettings(&prefered_width, &prefered_height,
115                                        &prefered_frame_rate) == 0) {
116      if (prefered_width > largest_width) {
117        largest_width = prefered_width;
118      }
119      if (prefered_height > largest_height) {
120        largest_height = prefered_height;
121      }
122      if (prefered_frame_rate > highest_frame_rate) {
123        highest_frame_rate = prefered_frame_rate;
124      }
125    }
126  }
127  *best_width = largest_width;
128  *best_height = largest_height;
129  *best_frame_rate = highest_frame_rate;
130  return 0;
131}
132
133int ViEFrameProviderBase::RegisterFrameCallback(
134    int observer_id, ViEFrameCallback* callback_object) {
135  assert(callback_object);
136  {
137    CriticalSectionScoped cs(provider_cs_.get());
138    if (std::find(frame_callbacks_.begin(), frame_callbacks_.end(),
139                  callback_object) != frame_callbacks_.end()) {
140      assert(false && "frameObserver already registered");
141      return -1;
142    }
143    frame_callbacks_.push_back(callback_object);
144  }
145  // Report current capture delay.
146  callback_object->DelayChanged(id_, frame_delay_);
147
148  // Notify implementer of this class that the callback list have changed.
149  FrameCallbackChanged();
150  return 0;
151}
152
153int ViEFrameProviderBase::DeregisterFrameCallback(
154    const ViEFrameCallback* callback_object) {
155  assert(callback_object);
156  CriticalSectionScoped cs(provider_cs_.get());
157
158  FrameCallbacks::iterator it = std::find(frame_callbacks_.begin(),
159                                          frame_callbacks_.end(),
160                                          callback_object);
161  if (it == frame_callbacks_.end()) {
162    return -1;
163  }
164  frame_callbacks_.erase(it);
165
166  // Notify implementer of this class that the callback list have changed.
167  FrameCallbackChanged();
168  return 0;
169}
170
171bool ViEFrameProviderBase::IsFrameCallbackRegistered(
172    const ViEFrameCallback* callback_object) {
173  assert(callback_object);
174
175  CriticalSectionScoped cs(provider_cs_.get());
176  return std::find(frame_callbacks_.begin(), frame_callbacks_.end(),
177                   callback_object) != frame_callbacks_.end();
178}
179
180int ViEFrameProviderBase::NumberOfRegisteredFrameCallbacks() {
181  CriticalSectionScoped cs(provider_cs_.get());
182  return frame_callbacks_.size();
183}
184}  // namespac webrtc
185