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