1// libjingle 2// Copyright 2004 Google Inc. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are met: 6// 7// 1. Redistributions of source code must retain the above copyright notice, 8// this list of conditions and the following disclaimer. 9// 2. Redistributions in binary form must reproduce the above copyright notice, 10// this list of conditions and the following disclaimer in the documentation 11// and/or other materials provided with the distribution. 12// 3. The name of the author may not be used to endorse or promote products 13// derived from this software without specific prior written permission. 14// 15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25// 26// Implementation of VideoRecorder and FileVideoCapturer. 27 28#include "talk/media/devices/filevideocapturer.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 35namespace cricket { 36 37///////////////////////////////////////////////////////////////////// 38// Implementation of class VideoRecorder 39///////////////////////////////////////////////////////////////////// 40bool VideoRecorder::Start(const std::string& filename, bool write_header) { 41 Stop(); 42 write_header_ = write_header; 43 int err; 44 if (!video_file_.Open(filename, "wb", &err)) { 45 LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err; 46 return false; 47 } 48 return true; 49} 50 51void VideoRecorder::Stop() { 52 video_file_.Close(); 53} 54 55bool VideoRecorder::RecordFrame(const CapturedFrame& frame) { 56 if (rtc::SS_CLOSED == video_file_.GetState()) { 57 LOG(LS_ERROR) << "File not opened yet"; 58 return false; 59 } 60 61 uint32 size = 0; 62 if (!frame.GetDataSize(&size)) { 63 LOG(LS_ERROR) << "Unable to calculate the data size of the frame"; 64 return false; 65 } 66 67 if (write_header_) { 68 // Convert the frame header to bytebuffer. 69 rtc::ByteBuffer buffer; 70 buffer.WriteUInt32(frame.width); 71 buffer.WriteUInt32(frame.height); 72 buffer.WriteUInt32(frame.fourcc); 73 buffer.WriteUInt32(frame.pixel_width); 74 buffer.WriteUInt32(frame.pixel_height); 75 buffer.WriteUInt64(frame.elapsed_time); 76 buffer.WriteUInt64(frame.time_stamp); 77 buffer.WriteUInt32(size); 78 79 // Write the bytebuffer to file. 80 if (rtc::SR_SUCCESS != video_file_.Write(buffer.Data(), 81 buffer.Length(), 82 NULL, 83 NULL)) { 84 LOG(LS_ERROR) << "Failed to write frame header"; 85 return false; 86 } 87 } 88 // Write the frame data to file. 89 if (rtc::SR_SUCCESS != video_file_.Write(frame.data, 90 size, 91 NULL, 92 NULL)) { 93 LOG(LS_ERROR) << "Failed to write frame data"; 94 return false; 95 } 96 97 return true; 98} 99 100/////////////////////////////////////////////////////////////////////// 101// Definition of private class FileReadThread that periodically reads 102// frames from a file. 103/////////////////////////////////////////////////////////////////////// 104class FileVideoCapturer::FileReadThread 105 : public rtc::Thread, public rtc::MessageHandler { 106 public: 107 explicit FileReadThread(FileVideoCapturer* capturer) 108 : capturer_(capturer), 109 finished_(false) { 110 } 111 112 virtual ~FileReadThread() { 113 Stop(); 114 } 115 116 // Override virtual method of parent Thread. Context: Worker Thread. 117 virtual void Run() { 118 // Read the first frame and start the message pump. The pump runs until 119 // Stop() is called externally or Quit() is called by OnMessage(). 120 int waiting_time_ms = 0; 121 if (capturer_ && capturer_->ReadFrame(true, &waiting_time_ms)) { 122 PostDelayed(waiting_time_ms, this); 123 Thread::Run(); 124 } 125 126 rtc::CritScope cs(&crit_); 127 finished_ = true; 128 } 129 130 // Override virtual method of parent MessageHandler. Context: Worker Thread. 131 virtual void OnMessage(rtc::Message* /*pmsg*/) { 132 int waiting_time_ms = 0; 133 if (capturer_ && capturer_->ReadFrame(false, &waiting_time_ms)) { 134 PostDelayed(waiting_time_ms, this); 135 } else { 136 Quit(); 137 } 138 } 139 140 // Check if Run() is finished. 141 bool Finished() const { 142 rtc::CritScope cs(&crit_); 143 return finished_; 144 } 145 146 private: 147 FileVideoCapturer* capturer_; 148 mutable rtc::CriticalSection crit_; 149 bool finished_; 150 151 DISALLOW_COPY_AND_ASSIGN(FileReadThread); 152}; 153 154///////////////////////////////////////////////////////////////////// 155// Implementation of class FileVideoCapturer 156///////////////////////////////////////////////////////////////////// 157static const int64 kNumNanoSecsPerMilliSec = 1000000; 158const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:"; 159 160FileVideoCapturer::FileVideoCapturer() 161 : frame_buffer_size_(0), 162 file_read_thread_(NULL), 163 repeat_(0), 164 start_time_ns_(0), 165 last_frame_timestamp_ns_(0), 166 ignore_framerate_(false) { 167} 168 169FileVideoCapturer::~FileVideoCapturer() { 170 Stop(); 171 delete[] static_cast<char*>(captured_frame_.data); 172} 173 174bool FileVideoCapturer::Init(const Device& device) { 175 if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) { 176 return false; 177 } 178 std::string filename(device.name); 179 if (IsRunning()) { 180 LOG(LS_ERROR) << "The file video capturer is already running"; 181 return false; 182 } 183 // Open the file. 184 int err; 185 if (!video_file_.Open(filename, "rb", &err)) { 186 LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err; 187 return false; 188 } 189 // Read the first frame's header to determine the supported format. 190 CapturedFrame frame; 191 if (rtc::SR_SUCCESS != ReadFrameHeader(&frame)) { 192 LOG(LS_ERROR) << "Failed to read the first frame header"; 193 video_file_.Close(); 194 return false; 195 } 196 // Seek back to the start of the file. 197 if (!video_file_.SetPosition(0)) { 198 LOG(LS_ERROR) << "Failed to seek back to beginning of the file"; 199 video_file_.Close(); 200 return false; 201 } 202 203 // Enumerate the supported formats. We have only one supported format. We set 204 // the frame interval to kMinimumInterval here. In Start(), if the capture 205 // format's interval is greater than kMinimumInterval, we use the interval; 206 // otherwise, we use the timestamp in the file to control the interval. 207 VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval, 208 frame.fourcc); 209 std::vector<VideoFormat> supported; 210 supported.push_back(format); 211 212 // TODO(thorcarpenter): Report the actual file video format as the supported 213 // format. Do not use kMinimumInterval as it conflicts with video adaptation. 214 SetId(device.id); 215 SetSupportedFormats(supported); 216 217 // TODO(wuwang): Design an E2E integration test for video adaptation, 218 // then remove the below call to disable the video adapter. 219 set_enable_video_adapter(false); 220 return true; 221} 222 223bool FileVideoCapturer::Init(const std::string& filename) { 224 return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename)); 225} 226 227CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) { 228 if (IsRunning()) { 229 LOG(LS_ERROR) << "The file video capturer is already running"; 230 return CS_FAILED; 231 } 232 233 if (rtc::SS_CLOSED == video_file_.GetState()) { 234 LOG(LS_ERROR) << "File not opened yet"; 235 return CS_NO_DEVICE; 236 } else if (!video_file_.SetPosition(0)) { 237 LOG(LS_ERROR) << "Failed to seek back to beginning of the file"; 238 return CS_FAILED; 239 } 240 241 SetCaptureFormat(&capture_format); 242 // Create a thread to read the file. 243 file_read_thread_ = new FileReadThread(this); 244 start_time_ns_ = kNumNanoSecsPerMilliSec * 245 static_cast<int64>(rtc::Time()); 246 bool ret = file_read_thread_->Start(); 247 if (ret) { 248 LOG(LS_INFO) << "File video capturer '" << GetId() << "' started"; 249 return CS_RUNNING; 250 } else { 251 LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start"; 252 return CS_FAILED; 253 } 254} 255 256bool FileVideoCapturer::IsRunning() { 257 return file_read_thread_ && !file_read_thread_->Finished(); 258} 259 260void FileVideoCapturer::Stop() { 261 if (file_read_thread_) { 262 file_read_thread_->Stop(); 263 file_read_thread_ = NULL; 264 LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped"; 265 } 266 SetCaptureFormat(NULL); 267} 268 269bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) { 270 if (!fourccs) { 271 return false; 272 } 273 274 fourccs->push_back(GetSupportedFormats()->at(0).fourcc); 275 return true; 276} 277 278rtc::StreamResult FileVideoCapturer::ReadFrameHeader( 279 CapturedFrame* frame) { 280 // We first read kFrameHeaderSize bytes from the file stream to a memory 281 // buffer, then construct a bytebuffer from the memory buffer, and finally 282 // read the frame header from the bytebuffer. 283 char header[CapturedFrame::kFrameHeaderSize]; 284 rtc::StreamResult sr; 285 size_t bytes_read; 286 int error; 287 sr = video_file_.Read(header, 288 CapturedFrame::kFrameHeaderSize, 289 &bytes_read, 290 &error); 291 LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr 292 << ", bytes read = " << bytes_read << ", error = " << error; 293 if (rtc::SR_SUCCESS == sr) { 294 if (CapturedFrame::kFrameHeaderSize != bytes_read) { 295 return rtc::SR_EOS; 296 } 297 rtc::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize); 298 buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->width)); 299 buffer.ReadUInt32(reinterpret_cast<uint32*>(&frame->height)); 300 buffer.ReadUInt32(&frame->fourcc); 301 buffer.ReadUInt32(&frame->pixel_width); 302 buffer.ReadUInt32(&frame->pixel_height); 303 buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->elapsed_time)); 304 buffer.ReadUInt64(reinterpret_cast<uint64*>(&frame->time_stamp)); 305 buffer.ReadUInt32(&frame->data_size); 306 } 307 308 return sr; 309} 310 311// Executed in the context of FileReadThread. 312bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) { 313 uint32 start_read_time_ms = rtc::Time(); 314 315 // 1. Signal the previously read frame to downstream. 316 if (!first_frame) { 317 captured_frame_.time_stamp = kNumNanoSecsPerMilliSec * 318 static_cast<int64>(start_read_time_ms); 319 captured_frame_.elapsed_time = captured_frame_.time_stamp - start_time_ns_; 320 SignalFrameCaptured(this, &captured_frame_); 321 } 322 323 // 2. Read the next frame. 324 if (rtc::SS_CLOSED == video_file_.GetState()) { 325 LOG(LS_ERROR) << "File not opened yet"; 326 return false; 327 } 328 // 2.1 Read the frame header. 329 rtc::StreamResult result = ReadFrameHeader(&captured_frame_); 330 if (rtc::SR_EOS == result) { // Loop back if repeat. 331 if (repeat_ != rtc::kForever) { 332 if (repeat_ > 0) { 333 --repeat_; 334 } else { 335 return false; 336 } 337 } 338 339 if (video_file_.SetPosition(0)) { 340 result = ReadFrameHeader(&captured_frame_); 341 } 342 } 343 if (rtc::SR_SUCCESS != result) { 344 LOG(LS_ERROR) << "Failed to read the frame header"; 345 return false; 346 } 347 // 2.2 Reallocate memory for the frame data if necessary. 348 if (frame_buffer_size_ < captured_frame_.data_size) { 349 frame_buffer_size_ = captured_frame_.data_size; 350 delete[] static_cast<char*>(captured_frame_.data); 351 captured_frame_.data = new char[frame_buffer_size_]; 352 } 353 // 2.3 Read the frame adata. 354 if (rtc::SR_SUCCESS != video_file_.Read(captured_frame_.data, 355 captured_frame_.data_size, 356 NULL, NULL)) { 357 LOG(LS_ERROR) << "Failed to read frame data"; 358 return false; 359 } 360 361 // 3. Decide how long to wait for the next frame. 362 *wait_time_ms = 0; 363 364 // If the capture format's interval is not kMinimumInterval, we use it to 365 // control the rate; otherwise, we use the timestamp in the file to control 366 // the rate. 367 if (!first_frame && !ignore_framerate_) { 368 int64 interval_ns = 369 GetCaptureFormat()->interval > VideoFormat::kMinimumInterval ? 370 GetCaptureFormat()->interval : 371 captured_frame_.time_stamp - last_frame_timestamp_ns_; 372 int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec); 373 interval_ms -= rtc::Time() - start_read_time_ms; 374 if (interval_ms > 0) { 375 *wait_time_ms = interval_ms; 376 } 377 } 378 // Keep the original timestamp read from the file. 379 last_frame_timestamp_ns_ = captured_frame_.time_stamp; 380 return true; 381} 382 383} // namespace cricket 384