/* * libjingle * Copyright 2004--2014 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "talk/media/devices/yuvframescapturer.h" #include "webrtc/base/bytebuffer.h" #include "webrtc/base/criticalsection.h" #include "webrtc/base/logging.h" #include "webrtc/base/thread.h" #include "webrtc/system_wrappers/include/clock.h" namespace cricket { /////////////////////////////////////////////////////////////////////// // Definition of private class YuvFramesThread that periodically generates // frames. /////////////////////////////////////////////////////////////////////// class YuvFramesCapturer::YuvFramesThread : public rtc::Thread, public rtc::MessageHandler { public: explicit YuvFramesThread(YuvFramesCapturer* capturer) : capturer_(capturer), finished_(false) { } virtual ~YuvFramesThread() { Stop(); } // Override virtual method of parent Thread. Context: Worker Thread. virtual void Run() { // Read the first frame and start the message pump. The pump runs until // Stop() is called externally or Quit() is called by OnMessage(). int waiting_time_ms = 0; if (capturer_) { capturer_->ReadFrame(true); PostDelayed(waiting_time_ms, this); Thread::Run(); } rtc::CritScope cs(&crit_); finished_ = true; } // Override virtual method of parent MessageHandler. Context: Worker Thread. virtual void OnMessage(rtc::Message* /*pmsg*/) { int waiting_time_ms = 0; if (capturer_) { capturer_->ReadFrame(false); PostDelayed(waiting_time_ms, this); } else { Quit(); } } // Check if Run() is finished. bool Finished() const { rtc::CritScope cs(&crit_); return finished_; } private: YuvFramesCapturer* capturer_; mutable rtc::CriticalSection crit_; bool finished_; RTC_DISALLOW_COPY_AND_ASSIGN(YuvFramesThread); }; ///////////////////////////////////////////////////////////////////// // Implementation of class YuvFramesCapturer. ///////////////////////////////////////////////////////////////////// const char* YuvFramesCapturer::kYuvFrameDeviceName = "YuvFramesGenerator"; // TODO(shaowei): allow width_ and height_ to be configurable. YuvFramesCapturer::YuvFramesCapturer() : frames_generator_thread(NULL), width_(640), height_(480), frame_index_(0), barcode_interval_(1) { } YuvFramesCapturer::~YuvFramesCapturer() { Stop(); delete[] static_cast(captured_frame_.data); } void YuvFramesCapturer::Init() { int size = width_ * height_; int qsize = size / 4; frame_generator_ = new YuvFrameGenerator(width_, height_, true); frame_data_size_ = size + 2 * qsize; captured_frame_.data = new char[frame_data_size_]; captured_frame_.fourcc = FOURCC_IYUV; captured_frame_.pixel_height = 1; captured_frame_.pixel_width = 1; captured_frame_.width = width_; captured_frame_.height = height_; captured_frame_.data_size = frame_data_size_; // Enumerate the supported formats. We have only one supported format. VideoFormat format(width_, height_, VideoFormat::kMinimumInterval, FOURCC_IYUV); std::vector supported; supported.push_back(format); SetSupportedFormats(supported); } CaptureState YuvFramesCapturer::Start(const VideoFormat& capture_format) { if (IsRunning()) { LOG(LS_ERROR) << "Yuv Frame Generator is already running"; return CS_FAILED; } SetCaptureFormat(&capture_format); barcode_reference_timestamp_millis_ = static_cast(rtc::Time()) * 1000; // Create a thread to generate frames. frames_generator_thread = new YuvFramesThread(this); bool ret = frames_generator_thread->Start(); if (ret) { LOG(LS_INFO) << "Yuv Frame Generator started"; return CS_RUNNING; } else { LOG(LS_ERROR) << "Yuv Frame Generator failed to start"; return CS_FAILED; } } bool YuvFramesCapturer::IsRunning() { return frames_generator_thread && !frames_generator_thread->Finished(); } void YuvFramesCapturer::Stop() { if (frames_generator_thread) { frames_generator_thread->Stop(); frames_generator_thread = NULL; LOG(LS_INFO) << "Yuv Frame Generator stopped"; } SetCaptureFormat(NULL); } bool YuvFramesCapturer::GetPreferredFourccs(std::vector* fourccs) { if (!fourccs) { return false; } fourccs->push_back(GetSupportedFormats()->at(0).fourcc); return true; } // Executed in the context of YuvFramesThread. void YuvFramesCapturer::ReadFrame(bool first_frame) { // 1. Signal the previously read frame to downstream. if (!first_frame) { SignalFrameCaptured(this, &captured_frame_); } uint8_t* buffer = new uint8_t[frame_data_size_]; frame_generator_->GenerateNextFrame(buffer, GetBarcodeValue()); frame_index_++; memmove(captured_frame_.data, buffer, frame_data_size_); delete[] buffer; } int32_t YuvFramesCapturer::GetBarcodeValue() { if (barcode_reference_timestamp_millis_ == -1 || frame_index_ % barcode_interval_ != 0) { return -1; } int64_t now_millis = static_cast(rtc::Time()) * 1000; return static_cast(now_millis - barcode_reference_timestamp_millis_); } } // namespace cricket