Home | History | Annotate | Download | only in devices
      1 /*
      2  * libjingle
      3  * Copyright 2004 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 // Implementation of VideoRecorder and FileVideoCapturer.
     29 
     30 #include "talk/media/devices/filevideocapturer.h"
     31 
     32 #include "webrtc/base/bytebuffer.h"
     33 #include "webrtc/base/criticalsection.h"
     34 #include "webrtc/base/logging.h"
     35 #include "webrtc/base/thread.h"
     36 
     37 namespace cricket {
     38 
     39 /////////////////////////////////////////////////////////////////////
     40 // Implementation of class VideoRecorder
     41 /////////////////////////////////////////////////////////////////////
     42 bool VideoRecorder::Start(const std::string& filename, bool write_header) {
     43   Stop();
     44   write_header_ = write_header;
     45   int err;
     46   if (!video_file_.Open(filename, "wb", &err)) {
     47     LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err;
     48     return false;
     49   }
     50   return true;
     51 }
     52 
     53 void VideoRecorder::Stop() {
     54   video_file_.Close();
     55 }
     56 
     57 bool VideoRecorder::RecordFrame(const CapturedFrame& frame) {
     58   if (rtc::SS_CLOSED == video_file_.GetState()) {
     59     LOG(LS_ERROR) << "File not opened yet";
     60     return false;
     61   }
     62 
     63   uint32_t size = 0;
     64   if (!frame.GetDataSize(&size)) {
     65     LOG(LS_ERROR) << "Unable to calculate the data size of the frame";
     66     return false;
     67   }
     68 
     69   if (write_header_) {
     70     // Convert the frame header to bytebuffer.
     71     rtc::ByteBuffer buffer;
     72     buffer.WriteUInt32(frame.width);
     73     buffer.WriteUInt32(frame.height);
     74     buffer.WriteUInt32(frame.fourcc);
     75     buffer.WriteUInt32(frame.pixel_width);
     76     buffer.WriteUInt32(frame.pixel_height);
     77     // Elapsed time is deprecated.
     78     const uint64_t dummy_elapsed_time = 0;
     79     buffer.WriteUInt64(dummy_elapsed_time);
     80     buffer.WriteUInt64(frame.time_stamp);
     81     buffer.WriteUInt32(size);
     82 
     83     // Write the bytebuffer to file.
     84     if (rtc::SR_SUCCESS != video_file_.Write(buffer.Data(),
     85                                                    buffer.Length(),
     86                                                    NULL,
     87                                                    NULL)) {
     88       LOG(LS_ERROR) << "Failed to write frame header";
     89       return false;
     90     }
     91   }
     92   // Write the frame data to file.
     93   if (rtc::SR_SUCCESS != video_file_.Write(frame.data,
     94                                                  size,
     95                                                  NULL,
     96                                                  NULL)) {
     97     LOG(LS_ERROR) << "Failed to write frame data";
     98     return false;
     99   }
    100 
    101   return true;
    102 }
    103 
    104 ///////////////////////////////////////////////////////////////////////
    105 // Definition of private class FileReadThread that periodically reads
    106 // frames from a file.
    107 ///////////////////////////////////////////////////////////////////////
    108 class FileVideoCapturer::FileReadThread
    109     : public rtc::Thread, public rtc::MessageHandler {
    110  public:
    111   explicit FileReadThread(FileVideoCapturer* capturer)
    112       : capturer_(capturer),
    113         finished_(false) {
    114   }
    115 
    116   virtual ~FileReadThread() {
    117     Stop();
    118   }
    119 
    120   // Override virtual method of parent Thread. Context: Worker Thread.
    121   virtual void Run() {
    122     // Read the first frame and start the message pump. The pump runs until
    123     // Stop() is called externally or Quit() is called by OnMessage().
    124     int waiting_time_ms = 0;
    125     if (capturer_ && capturer_->ReadFrame(true, &waiting_time_ms)) {
    126       PostDelayed(waiting_time_ms, this);
    127       Thread::Run();
    128     }
    129 
    130     rtc::CritScope cs(&crit_);
    131     finished_ = true;
    132   }
    133 
    134   // Override virtual method of parent MessageHandler. Context: Worker Thread.
    135   virtual void OnMessage(rtc::Message* /*pmsg*/) {
    136     int waiting_time_ms = 0;
    137     if (capturer_ && capturer_->ReadFrame(false, &waiting_time_ms)) {
    138       PostDelayed(waiting_time_ms, this);
    139     } else {
    140       Quit();
    141     }
    142   }
    143 
    144   // Check if Run() is finished.
    145   bool Finished() const {
    146     rtc::CritScope cs(&crit_);
    147     return finished_;
    148   }
    149 
    150  private:
    151   FileVideoCapturer* capturer_;
    152   mutable rtc::CriticalSection crit_;
    153   bool finished_;
    154 
    155   RTC_DISALLOW_COPY_AND_ASSIGN(FileReadThread);
    156 };
    157 
    158 /////////////////////////////////////////////////////////////////////
    159 // Implementation of class FileVideoCapturer
    160 /////////////////////////////////////////////////////////////////////
    161 static const int64_t kNumNanoSecsPerMilliSec = 1000000;
    162 const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:";
    163 
    164 FileVideoCapturer::FileVideoCapturer()
    165     : frame_buffer_size_(0),
    166       file_read_thread_(NULL),
    167       repeat_(0),
    168       last_frame_timestamp_ns_(0),
    169       ignore_framerate_(false) {
    170 }
    171 
    172 FileVideoCapturer::~FileVideoCapturer() {
    173   Stop();
    174   delete[] static_cast<char*>(captured_frame_.data);
    175 }
    176 
    177 bool FileVideoCapturer::Init(const Device& device) {
    178   if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
    179     return false;
    180   }
    181   std::string filename(device.name);
    182   if (IsRunning()) {
    183     LOG(LS_ERROR) << "The file video capturer is already running";
    184     return false;
    185   }
    186   // Open the file.
    187   int err;
    188   if (!video_file_.Open(filename, "rb", &err)) {
    189     LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err;
    190     return false;
    191   }
    192   // Read the first frame's header to determine the supported format.
    193   CapturedFrame frame;
    194   if (rtc::SR_SUCCESS != ReadFrameHeader(&frame)) {
    195     LOG(LS_ERROR) << "Failed to read the first frame header";
    196     video_file_.Close();
    197     return false;
    198   }
    199   // Seek back to the start of the file.
    200   if (!video_file_.SetPosition(0)) {
    201     LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
    202     video_file_.Close();
    203     return false;
    204   }
    205 
    206   // Enumerate the supported formats. We have only one supported format. We set
    207   // the frame interval to kMinimumInterval here. In Start(), if the capture
    208   // format's interval is greater than kMinimumInterval, we use the interval;
    209   // otherwise, we use the timestamp in the file to control the interval.
    210   VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval,
    211                      frame.fourcc);
    212   std::vector<VideoFormat> supported;
    213   supported.push_back(format);
    214 
    215   // TODO(thorcarpenter): Report the actual file video format as the supported
    216   // format. Do not use kMinimumInterval as it conflicts with video adaptation.
    217   SetId(device.id);
    218   SetSupportedFormats(supported);
    219 
    220   // TODO(wuwang): Design an E2E integration test for video adaptation,
    221   // then remove the below call to disable the video adapter.
    222   set_enable_video_adapter(false);
    223   return true;
    224 }
    225 
    226 bool FileVideoCapturer::Init(const std::string& filename) {
    227   return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename));
    228 }
    229 
    230 CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) {
    231   if (IsRunning()) {
    232     LOG(LS_ERROR) << "The file video capturer is already running";
    233     return CS_FAILED;
    234   }
    235 
    236   if (rtc::SS_CLOSED == video_file_.GetState()) {
    237     LOG(LS_ERROR) << "File not opened yet";
    238     return CS_NO_DEVICE;
    239   } else if (!video_file_.SetPosition(0)) {
    240     LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
    241     return CS_FAILED;
    242   }
    243 
    244   SetCaptureFormat(&capture_format);
    245   // Create a thread to read the file.
    246   file_read_thread_ = new FileReadThread(this);
    247   bool ret = file_read_thread_->Start();
    248   if (ret) {
    249     LOG(LS_INFO) << "File video capturer '" << GetId() << "' started";
    250     return CS_RUNNING;
    251   } else {
    252     LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start";
    253     return CS_FAILED;
    254   }
    255 }
    256 
    257 bool FileVideoCapturer::IsRunning() {
    258   return file_read_thread_ && !file_read_thread_->Finished();
    259 }
    260 
    261 void FileVideoCapturer::Stop() {
    262   if (file_read_thread_) {
    263     file_read_thread_->Stop();
    264     file_read_thread_ = NULL;
    265     LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped";
    266   }
    267   SetCaptureFormat(NULL);
    268 }
    269 
    270 bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
    271   if (!fourccs) {
    272     return false;
    273   }
    274 
    275   fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
    276   return true;
    277 }
    278 
    279 rtc::StreamResult FileVideoCapturer::ReadFrameHeader(
    280     CapturedFrame* frame) {
    281   // We first read kFrameHeaderSize bytes from the file stream to a memory
    282   // buffer, then construct a bytebuffer from the memory buffer, and finally
    283   // read the frame header from the bytebuffer.
    284   char header[CapturedFrame::kFrameHeaderSize];
    285   rtc::StreamResult sr;
    286   size_t bytes_read;
    287   int error;
    288   sr = video_file_.Read(header,
    289                         CapturedFrame::kFrameHeaderSize,
    290                         &bytes_read,
    291                         &error);
    292   LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr
    293                   << ", bytes read = " << bytes_read << ", error = " << error;
    294   if (rtc::SR_SUCCESS == sr) {
    295     if (CapturedFrame::kFrameHeaderSize != bytes_read) {
    296       return rtc::SR_EOS;
    297     }
    298     rtc::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize);
    299     buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->width));
    300     buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->height));
    301     buffer.ReadUInt32(&frame->fourcc);
    302     buffer.ReadUInt32(&frame->pixel_width);
    303     buffer.ReadUInt32(&frame->pixel_height);
    304     // Elapsed time is deprecated.
    305     uint64_t dummy_elapsed_time;
    306     buffer.ReadUInt64(&dummy_elapsed_time);
    307     buffer.ReadUInt64(reinterpret_cast<uint64_t*>(&frame->time_stamp));
    308     buffer.ReadUInt32(&frame->data_size);
    309   }
    310 
    311   return sr;
    312 }
    313 
    314 // Executed in the context of FileReadThread.
    315 bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) {
    316   uint32_t start_read_time_ms = rtc::Time();
    317 
    318   // 1. Signal the previously read frame to downstream.
    319   if (!first_frame) {
    320     captured_frame_.time_stamp =
    321         kNumNanoSecsPerMilliSec * static_cast<int64_t>(start_read_time_ms);
    322     SignalFrameCaptured(this, &captured_frame_);
    323   }
    324 
    325   // 2. Read the next frame.
    326   if (rtc::SS_CLOSED == video_file_.GetState()) {
    327     LOG(LS_ERROR) << "File not opened yet";
    328     return false;
    329   }
    330   // 2.1 Read the frame header.
    331   rtc::StreamResult result = ReadFrameHeader(&captured_frame_);
    332   if (rtc::SR_EOS == result) {  // Loop back if repeat.
    333     if (repeat_ != kForever) {
    334       if (repeat_ > 0) {
    335         --repeat_;
    336       } else {
    337         return false;
    338       }
    339     }
    340 
    341     if (video_file_.SetPosition(0)) {
    342       result = ReadFrameHeader(&captured_frame_);
    343     }
    344   }
    345   if (rtc::SR_SUCCESS != result) {
    346     LOG(LS_ERROR) << "Failed to read the frame header";
    347     return false;
    348   }
    349   // 2.2 Reallocate memory for the frame data if necessary.
    350   if (frame_buffer_size_ < captured_frame_.data_size) {
    351     frame_buffer_size_ = captured_frame_.data_size;
    352     delete[] static_cast<char*>(captured_frame_.data);
    353     captured_frame_.data = new char[frame_buffer_size_];
    354   }
    355   // 2.3 Read the frame adata.
    356   if (rtc::SR_SUCCESS != video_file_.Read(captured_frame_.data,
    357                                                 captured_frame_.data_size,
    358                                                 NULL, NULL)) {
    359     LOG(LS_ERROR) << "Failed to read frame data";
    360     return false;
    361   }
    362 
    363   // 3. Decide how long to wait for the next frame.
    364   *wait_time_ms = 0;
    365 
    366   // If the capture format's interval is not kMinimumInterval, we use it to
    367   // control the rate; otherwise, we use the timestamp in the file to control
    368   // the rate.
    369   if (!first_frame && !ignore_framerate_) {
    370     int64_t interval_ns =
    371         GetCaptureFormat()->interval > VideoFormat::kMinimumInterval
    372             ? GetCaptureFormat()->interval
    373             : captured_frame_.time_stamp - last_frame_timestamp_ns_;
    374     int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec);
    375     interval_ms -= rtc::Time() - start_read_time_ms;
    376     if (interval_ms > 0) {
    377       *wait_time_ms = interval_ms;
    378     }
    379   }
    380   // Keep the original timestamp read from the file.
    381   last_frame_timestamp_ns_ = captured_frame_.time_stamp;
    382   return true;
    383 }
    384 
    385 }  // namespace cricket
    386