Index: talk/media/devices/filevideocapturer.cc |
diff --git a/talk/media/devices/filevideocapturer.cc b/talk/media/devices/filevideocapturer.cc |
deleted file mode 100644 |
index 8849a09b258f1d6f417deafee0439187bf26c945..0000000000000000000000000000000000000000 |
--- a/talk/media/devices/filevideocapturer.cc |
+++ /dev/null |
@@ -1,385 +0,0 @@ |
-/* |
- * libjingle |
- * Copyright 2004 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. |
- */ |
- |
-// Implementation of VideoRecorder and FileVideoCapturer. |
- |
-#include "talk/media/devices/filevideocapturer.h" |
- |
-#include "webrtc/base/bytebuffer.h" |
-#include "webrtc/base/criticalsection.h" |
-#include "webrtc/base/logging.h" |
-#include "webrtc/base/thread.h" |
- |
-namespace cricket { |
- |
-///////////////////////////////////////////////////////////////////// |
-// Implementation of class VideoRecorder |
-///////////////////////////////////////////////////////////////////// |
-bool VideoRecorder::Start(const std::string& filename, bool write_header) { |
- Stop(); |
- write_header_ = write_header; |
- int err; |
- if (!video_file_.Open(filename, "wb", &err)) { |
- LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err; |
- return false; |
- } |
- return true; |
-} |
- |
-void VideoRecorder::Stop() { |
- video_file_.Close(); |
-} |
- |
-bool VideoRecorder::RecordFrame(const CapturedFrame& frame) { |
- if (rtc::SS_CLOSED == video_file_.GetState()) { |
- LOG(LS_ERROR) << "File not opened yet"; |
- return false; |
- } |
- |
- uint32_t size = 0; |
- if (!frame.GetDataSize(&size)) { |
- LOG(LS_ERROR) << "Unable to calculate the data size of the frame"; |
- return false; |
- } |
- |
- if (write_header_) { |
- // Convert the frame header to bytebuffer. |
- rtc::ByteBuffer buffer; |
- buffer.WriteUInt32(frame.width); |
- buffer.WriteUInt32(frame.height); |
- buffer.WriteUInt32(frame.fourcc); |
- buffer.WriteUInt32(frame.pixel_width); |
- buffer.WriteUInt32(frame.pixel_height); |
- // Elapsed time is deprecated. |
- const uint64_t dummy_elapsed_time = 0; |
- buffer.WriteUInt64(dummy_elapsed_time); |
- buffer.WriteUInt64(frame.time_stamp); |
- buffer.WriteUInt32(size); |
- |
- // Write the bytebuffer to file. |
- if (rtc::SR_SUCCESS != video_file_.Write(buffer.Data(), |
- buffer.Length(), |
- NULL, |
- NULL)) { |
- LOG(LS_ERROR) << "Failed to write frame header"; |
- return false; |
- } |
- } |
- // Write the frame data to file. |
- if (rtc::SR_SUCCESS != video_file_.Write(frame.data, |
- size, |
- NULL, |
- NULL)) { |
- LOG(LS_ERROR) << "Failed to write frame data"; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-/////////////////////////////////////////////////////////////////////// |
-// Definition of private class FileReadThread that periodically reads |
-// frames from a file. |
-/////////////////////////////////////////////////////////////////////// |
-class FileVideoCapturer::FileReadThread |
- : public rtc::Thread, public rtc::MessageHandler { |
- public: |
- explicit FileReadThread(FileVideoCapturer* capturer) |
- : capturer_(capturer), |
- finished_(false) { |
- } |
- |
- virtual ~FileReadThread() { |
- 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, &waiting_time_ms)) { |
- 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, &waiting_time_ms)) { |
- PostDelayed(waiting_time_ms, this); |
- } else { |
- Quit(); |
- } |
- } |
- |
- // Check if Run() is finished. |
- bool Finished() const { |
- rtc::CritScope cs(&crit_); |
- return finished_; |
- } |
- |
- private: |
- FileVideoCapturer* capturer_; |
- rtc::CriticalSection crit_; |
- bool finished_; |
- |
- RTC_DISALLOW_COPY_AND_ASSIGN(FileReadThread); |
-}; |
- |
-///////////////////////////////////////////////////////////////////// |
-// Implementation of class FileVideoCapturer |
-///////////////////////////////////////////////////////////////////// |
-static const int64_t kNumNanoSecsPerMilliSec = 1000000; |
-const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:"; |
- |
-FileVideoCapturer::FileVideoCapturer() |
- : frame_buffer_size_(0), |
- file_read_thread_(NULL), |
- repeat_(0), |
- last_frame_timestamp_ns_(0), |
- ignore_framerate_(false) { |
-} |
- |
-FileVideoCapturer::~FileVideoCapturer() { |
- Stop(); |
- delete[] static_cast<char*>(captured_frame_.data); |
-} |
- |
-bool FileVideoCapturer::Init(const Device& device) { |
- if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) { |
- return false; |
- } |
- std::string filename(device.name); |
- if (IsRunning()) { |
- LOG(LS_ERROR) << "The file video capturer is already running"; |
- return false; |
- } |
- // Open the file. |
- int err; |
- if (!video_file_.Open(filename, "rb", &err)) { |
- LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err; |
- return false; |
- } |
- // Read the first frame's header to determine the supported format. |
- CapturedFrame frame; |
- if (rtc::SR_SUCCESS != ReadFrameHeader(&frame)) { |
- LOG(LS_ERROR) << "Failed to read the first frame header"; |
- video_file_.Close(); |
- return false; |
- } |
- // Seek back to the start of the file. |
- if (!video_file_.SetPosition(0)) { |
- LOG(LS_ERROR) << "Failed to seek back to beginning of the file"; |
- video_file_.Close(); |
- return false; |
- } |
- |
- // Enumerate the supported formats. We have only one supported format. We set |
- // the frame interval to kMinimumInterval here. In Start(), if the capture |
- // format's interval is greater than kMinimumInterval, we use the interval; |
- // otherwise, we use the timestamp in the file to control the interval. |
- VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval, |
- frame.fourcc); |
- std::vector<VideoFormat> supported; |
- supported.push_back(format); |
- |
- // TODO(thorcarpenter): Report the actual file video format as the supported |
- // format. Do not use kMinimumInterval as it conflicts with video adaptation. |
- SetId(device.id); |
- SetSupportedFormats(supported); |
- |
- // TODO(wuwang): Design an E2E integration test for video adaptation, |
- // then remove the below call to disable the video adapter. |
- set_enable_video_adapter(false); |
- return true; |
-} |
- |
-bool FileVideoCapturer::Init(const std::string& filename) { |
- return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename)); |
-} |
- |
-CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) { |
- if (IsRunning()) { |
- LOG(LS_ERROR) << "The file video capturer is already running"; |
- return CS_FAILED; |
- } |
- |
- if (rtc::SS_CLOSED == video_file_.GetState()) { |
- LOG(LS_ERROR) << "File not opened yet"; |
- return CS_NO_DEVICE; |
- } else if (!video_file_.SetPosition(0)) { |
- LOG(LS_ERROR) << "Failed to seek back to beginning of the file"; |
- return CS_FAILED; |
- } |
- |
- SetCaptureFormat(&capture_format); |
- // Create a thread to read the file. |
- file_read_thread_ = new FileReadThread(this); |
- bool ret = file_read_thread_->Start(); |
- if (ret) { |
- LOG(LS_INFO) << "File video capturer '" << GetId() << "' started"; |
- return CS_RUNNING; |
- } else { |
- LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start"; |
- return CS_FAILED; |
- } |
-} |
- |
-bool FileVideoCapturer::IsRunning() { |
- return file_read_thread_ && !file_read_thread_->Finished(); |
-} |
- |
-void FileVideoCapturer::Stop() { |
- if (file_read_thread_) { |
- file_read_thread_->Stop(); |
- file_read_thread_ = NULL; |
- LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped"; |
- } |
- SetCaptureFormat(NULL); |
-} |
- |
-bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) { |
- if (!fourccs) { |
- return false; |
- } |
- |
- fourccs->push_back(GetSupportedFormats()->at(0).fourcc); |
- return true; |
-} |
- |
-rtc::StreamResult FileVideoCapturer::ReadFrameHeader( |
- CapturedFrame* frame) { |
- // We first read kFrameHeaderSize bytes from the file stream to a memory |
- // buffer, then construct a bytebuffer from the memory buffer, and finally |
- // read the frame header from the bytebuffer. |
- char header[CapturedFrame::kFrameHeaderSize]; |
- rtc::StreamResult sr; |
- size_t bytes_read; |
- int error; |
- sr = video_file_.Read(header, |
- CapturedFrame::kFrameHeaderSize, |
- &bytes_read, |
- &error); |
- LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr |
- << ", bytes read = " << bytes_read << ", error = " << error; |
- if (rtc::SR_SUCCESS == sr) { |
- if (CapturedFrame::kFrameHeaderSize != bytes_read) { |
- return rtc::SR_EOS; |
- } |
- rtc::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize); |
- buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->width)); |
- buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->height)); |
- buffer.ReadUInt32(&frame->fourcc); |
- buffer.ReadUInt32(&frame->pixel_width); |
- buffer.ReadUInt32(&frame->pixel_height); |
- // Elapsed time is deprecated. |
- uint64_t dummy_elapsed_time; |
- buffer.ReadUInt64(&dummy_elapsed_time); |
- buffer.ReadUInt64(reinterpret_cast<uint64_t*>(&frame->time_stamp)); |
- buffer.ReadUInt32(&frame->data_size); |
- } |
- |
- return sr; |
-} |
- |
-// Executed in the context of FileReadThread. |
-bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) { |
- uint32_t start_read_time_ms = rtc::Time(); |
- |
- // 1. Signal the previously read frame to downstream. |
- if (!first_frame) { |
- captured_frame_.time_stamp = |
- kNumNanoSecsPerMilliSec * static_cast<int64_t>(start_read_time_ms); |
- SignalFrameCaptured(this, &captured_frame_); |
- } |
- |
- // 2. Read the next frame. |
- if (rtc::SS_CLOSED == video_file_.GetState()) { |
- LOG(LS_ERROR) << "File not opened yet"; |
- return false; |
- } |
- // 2.1 Read the frame header. |
- rtc::StreamResult result = ReadFrameHeader(&captured_frame_); |
- if (rtc::SR_EOS == result) { // Loop back if repeat. |
- if (repeat_ != kForever) { |
- if (repeat_ > 0) { |
- --repeat_; |
- } else { |
- return false; |
- } |
- } |
- |
- if (video_file_.SetPosition(0)) { |
- result = ReadFrameHeader(&captured_frame_); |
- } |
- } |
- if (rtc::SR_SUCCESS != result) { |
- LOG(LS_ERROR) << "Failed to read the frame header"; |
- return false; |
- } |
- // 2.2 Reallocate memory for the frame data if necessary. |
- if (frame_buffer_size_ < captured_frame_.data_size) { |
- frame_buffer_size_ = captured_frame_.data_size; |
- delete[] static_cast<char*>(captured_frame_.data); |
- captured_frame_.data = new char[frame_buffer_size_]; |
- } |
- // 2.3 Read the frame adata. |
- if (rtc::SR_SUCCESS != video_file_.Read(captured_frame_.data, |
- captured_frame_.data_size, |
- NULL, NULL)) { |
- LOG(LS_ERROR) << "Failed to read frame data"; |
- return false; |
- } |
- |
- // 3. Decide how long to wait for the next frame. |
- *wait_time_ms = 0; |
- |
- // If the capture format's interval is not kMinimumInterval, we use it to |
- // control the rate; otherwise, we use the timestamp in the file to control |
- // the rate. |
- if (!first_frame && !ignore_framerate_) { |
- int64_t interval_ns = |
- GetCaptureFormat()->interval > VideoFormat::kMinimumInterval |
- ? GetCaptureFormat()->interval |
- : captured_frame_.time_stamp - last_frame_timestamp_ns_; |
- int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec); |
- interval_ms -= rtc::Time() - start_read_time_ms; |
- if (interval_ms > 0) { |
- *wait_time_ms = interval_ms; |
- } |
- } |
- // Keep the original timestamp read from the file. |
- last_frame_timestamp_ns_ = captured_frame_.time_stamp; |
- return true; |
-} |
- |
-} // namespace cricket |