1/*
2 * libjingle
3 * Copyright 2004--2014 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#include "talk/media/devices/yuvframescapturer.h"
29
30#include "webrtc/base/bytebuffer.h"
31#include "webrtc/base/criticalsection.h"
32#include "webrtc/base/logging.h"
33#include "webrtc/base/thread.h"
34
35#include "webrtc/system_wrappers/include/clock.h"
36
37namespace cricket {
38///////////////////////////////////////////////////////////////////////
39// Definition of private class YuvFramesThread that periodically generates
40// frames.
41///////////////////////////////////////////////////////////////////////
42class YuvFramesCapturer::YuvFramesThread
43    : public rtc::Thread, public rtc::MessageHandler {
44 public:
45  explicit YuvFramesThread(YuvFramesCapturer* capturer)
46      : capturer_(capturer),
47        finished_(false) {
48  }
49
50  virtual ~YuvFramesThread() {
51    Stop();
52  }
53
54  // Override virtual method of parent Thread. Context: Worker Thread.
55  virtual void Run() {
56    // Read the first frame and start the message pump. The pump runs until
57    // Stop() is called externally or Quit() is called by OnMessage().
58    int waiting_time_ms = 0;
59    if (capturer_) {
60      capturer_->ReadFrame(true);
61      PostDelayed(waiting_time_ms, this);
62      Thread::Run();
63    }
64
65    rtc::CritScope cs(&crit_);
66    finished_ = true;
67  }
68
69  // Override virtual method of parent MessageHandler. Context: Worker Thread.
70  virtual void OnMessage(rtc::Message* /*pmsg*/) {
71    int waiting_time_ms = 0;
72    if (capturer_) {
73      capturer_->ReadFrame(false);
74      PostDelayed(waiting_time_ms, this);
75    } else {
76      Quit();
77    }
78  }
79
80  // Check if Run() is finished.
81  bool Finished() const {
82    rtc::CritScope cs(&crit_);
83    return finished_;
84  }
85
86 private:
87  YuvFramesCapturer* capturer_;
88  mutable rtc::CriticalSection crit_;
89  bool finished_;
90
91  RTC_DISALLOW_COPY_AND_ASSIGN(YuvFramesThread);
92};
93
94/////////////////////////////////////////////////////////////////////
95// Implementation of class YuvFramesCapturer.
96/////////////////////////////////////////////////////////////////////
97
98const char* YuvFramesCapturer::kYuvFrameDeviceName = "YuvFramesGenerator";
99
100// TODO(shaowei): allow width_ and height_ to be configurable.
101YuvFramesCapturer::YuvFramesCapturer()
102    : frames_generator_thread(NULL),
103      width_(640),
104      height_(480),
105      frame_index_(0),
106      barcode_interval_(1) {
107}
108
109YuvFramesCapturer::~YuvFramesCapturer() {
110  Stop();
111  delete[] static_cast<char*>(captured_frame_.data);
112}
113
114void YuvFramesCapturer::Init() {
115  int size = width_ * height_;
116  int qsize = size / 4;
117  frame_generator_ = new YuvFrameGenerator(width_, height_, true);
118  frame_data_size_ = size + 2 * qsize;
119  captured_frame_.data = new char[frame_data_size_];
120  captured_frame_.fourcc = FOURCC_IYUV;
121  captured_frame_.pixel_height = 1;
122  captured_frame_.pixel_width = 1;
123  captured_frame_.width = width_;
124  captured_frame_.height = height_;
125  captured_frame_.data_size = frame_data_size_;
126
127  // Enumerate the supported formats. We have only one supported format.
128  VideoFormat format(width_, height_, VideoFormat::kMinimumInterval,
129                     FOURCC_IYUV);
130  std::vector<VideoFormat> supported;
131  supported.push_back(format);
132  SetSupportedFormats(supported);
133}
134
135CaptureState YuvFramesCapturer::Start(const VideoFormat& capture_format) {
136  if (IsRunning()) {
137    LOG(LS_ERROR) << "Yuv Frame Generator is already running";
138    return CS_FAILED;
139  }
140  SetCaptureFormat(&capture_format);
141
142  barcode_reference_timestamp_millis_ =
143      static_cast<int64_t>(rtc::Time()) * 1000;
144  // Create a thread to generate frames.
145  frames_generator_thread = new YuvFramesThread(this);
146  bool ret = frames_generator_thread->Start();
147  if (ret) {
148    LOG(LS_INFO) << "Yuv Frame Generator started";
149    return CS_RUNNING;
150  } else {
151    LOG(LS_ERROR) << "Yuv Frame Generator failed to start";
152    return CS_FAILED;
153  }
154}
155
156bool YuvFramesCapturer::IsRunning() {
157  return frames_generator_thread && !frames_generator_thread->Finished();
158}
159
160void YuvFramesCapturer::Stop() {
161  if (frames_generator_thread) {
162    frames_generator_thread->Stop();
163    frames_generator_thread = NULL;
164    LOG(LS_INFO) << "Yuv Frame Generator stopped";
165  }
166  SetCaptureFormat(NULL);
167}
168
169bool YuvFramesCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
170  if (!fourccs) {
171    return false;
172  }
173  fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
174  return true;
175}
176
177// Executed in the context of YuvFramesThread.
178void YuvFramesCapturer::ReadFrame(bool first_frame) {
179  // 1. Signal the previously read frame to downstream.
180  if (!first_frame) {
181    SignalFrameCaptured(this, &captured_frame_);
182  }
183  uint8_t* buffer = new uint8_t[frame_data_size_];
184  frame_generator_->GenerateNextFrame(buffer, GetBarcodeValue());
185  frame_index_++;
186  memmove(captured_frame_.data, buffer, frame_data_size_);
187  delete[] buffer;
188}
189
190int32_t YuvFramesCapturer::GetBarcodeValue() {
191  if (barcode_reference_timestamp_millis_ == -1 ||
192       frame_index_ % barcode_interval_ != 0) {
193     return -1;
194  }
195  int64_t now_millis = static_cast<int64_t>(rtc::Time()) * 1000;
196  return static_cast<int32_t>(now_millis - barcode_reference_timestamp_millis_);
197}
198
199}  // namespace cricket
200